VPI - Vision Programming Interface

3.2 版本发布

基准测试

概述

此应用程序演示了如何正确测量处理 VPI 任务所花费的时间。它遵循了 基准测试方法 章节中描述的简化版本。

简而言之,此示例返回 5x5 高斯滤波器 算法执行时间的一系列测量值的中位数。每次测量包括运行该算法 50 次并取平均运行时间。这种批量测量和取中位数的形式可以得到更稳定的运行时间值,因为它排除了外部干扰因素。

说明

用法是

./vpi_sample_05_benchmark <backend>

其中

  • backend: 可以是 cpucudapva;它定义了将执行处理的后端。

这是一个示例

./vpi_sample_05_benchmark cuda

这是使用 CUDA 后端。尝试其他后端以查看它们之间的处理时间差异。

结果

注意
下面显示的基准测试结果仅用于演示目的,因此,我们在此处不指定所使用的硬件。您应该在要从中收集基准测试信息的平台上运行此示例。

CPU 后端

输入尺寸:1920 x 1080
图像格式:VPI_IMAGE_FORMAT_U16
算法:5x5 高斯滤波器
每次调用的大概运行时间:2.368492 毫秒
#define VPI_IMAGE_FORMAT_U16
具有一个 16 位无符号整数通道的单平面。

CUDA 后端

输入尺寸:1920 x 1080
图像格式:VPI_IMAGE_FORMAT_U16
算法:5x5 高斯滤波器
每次调用的大概运行时间:0.043012 毫秒

PVA 后端

输入尺寸:1920 x 1080
图像格式:VPI_IMAGE_FORMAT_U16
算法:5x5 高斯滤波器
NVMEDIA_ARRAY: 53, 版本 2.1
NVMEDIA_VPI : 172, 版本 2.4
每次调用的大概运行时间:1.692010 毫秒

源代码

为了方便起见,这里提供了也安装在 samples 目录中的代码。

语言
29 #include <vpi/Event.h>
30 #include <vpi/Image.h>
31 #include <vpi/Status.h>
32 #include <vpi/Stream.h>
34 
35 #include <algorithm>
36 #include <cstdio>
37 #include <iostream>
38 #include <sstream>
39 #include <vector>
40 
41 #define CHECK_STATUS(STMT) \
42  do \
43  { \
44  VPIStatus status = (STMT); \
45  if (status != VPI_SUCCESS) \
46  { \
47  char buffer[VPI_MAX_STATUS_MESSAGE_LENGTH]; \
48  vpiGetLastStatusMessage(buffer, sizeof(buffer)); \
49  std::ostringstream ss; \
50  ss << "" #STMT "\n"; \
51  ss << vpiStatusGetName(status) << ": " << buffer; \
52  throw std::runtime_error(ss.str()); \
53  } \
54  } while (0);
55 
56 int main(int argc, char *argv[])
57 {
58  VPIImage image = NULL;
59  VPIImage blurred = NULL;
60  VPIStream stream = NULL;
61 
62  VPIEvent evStart = NULL;
63  VPIEvent evStop = NULL;
64 
65  int retval = 0;
66 
67  try
68  {
69  // 1. Processing of command line parameters -----------
70 
71  if (argc != 2)
72  {
73  throw std::runtime_error(std::string("Usage: ") + argv[0] + " <cpu|pva|cuda>");
74  }
75 
76  std::string strBackend = argv[1];
77 
78  // Parse the backend
79  VPIBackend backend;
80 
81  if (strBackend == "cpu")
82  {
83  backend = VPI_BACKEND_CPU;
84  }
85  else if (strBackend == "cuda")
86  {
87  backend = VPI_BACKEND_CUDA;
88  }
89  else if (strBackend == "pva")
90  {
91  backend = VPI_BACKEND_PVA;
92  }
93  else
94  {
95  throw std::runtime_error("Backend '" + strBackend +
96  "' not recognized, it must be either cpu, cuda or pva.");
97  }
98 
99  // 2. Initialization stage ----------------------
100 
101  // Create the stream for only the target backend.
102  uint64_t streamFlags = (uint64_t)backend | VPI_REQUIRE_BACKENDS;
103  CHECK_STATUS(vpiStreamCreate(streamFlags, &stream));
104 
105  int width = 1920, height = 1080;
107 
108  std::cout << "Input size: " << width << " x " << height << '\n'
109  << "Image format: " << vpiImageFormatGetName(imgFormat) << '\n'
110  << "Algorithm: 5x5 Gaussian Filter on " << strBackend << std::endl;
111 
112  // Memory flags set to guarantee top performance.
113  // Only the benchmarked backend is enabled, and memories
114  // are guaranteed to be used by only one stream.
115  uint64_t memFlags = (uint64_t)backend | VPI_EXCLUSIVE_STREAM_ACCESS;
116 
117  // Create image with zero content
118  CHECK_STATUS(vpiImageCreate(width, height, imgFormat, memFlags, &image));
119 
120  // Create a temporary image convolved with a low-pass filter.
121  CHECK_STATUS(vpiImageCreate(width, height, imgFormat, memFlags, &blurred));
122 
123  // Create the events we'll need to get timing info
124  CHECK_STATUS(vpiEventCreate(backend, &evStart));
125  CHECK_STATUS(vpiEventCreate(backend, &evStop));
126 
127  // 3. Gather timings --------------------
128 
129  const int BATCH_COUNT = 20;
130  const int AVERAGING_COUNT = 50;
131 
132  // Collect measurements for each execution batch
133  std::vector<float> timingsMS;
134  for (int batch = 0; batch < BATCH_COUNT; ++batch)
135  {
136  // Record stream queue when we start processing
137  CHECK_STATUS(vpiEventRecord(evStart, stream));
138 
139  // Get the average running time within this batch.
140  for (int i = 0; i < AVERAGING_COUNT; ++i)
141  {
142  // Call the algorithm to be measured.
143  CHECK_STATUS(vpiSubmitGaussianFilter(stream, backend, image, blurred, 5, 5, 1, 1, VPI_BORDER_ZERO));
144  }
145 
146  // Record stream queue just after blurring
147  CHECK_STATUS(vpiEventRecord(evStop, stream));
148 
149  // Wait until the batch processing is done
150  CHECK_STATUS(vpiEventSync(evStop));
151 
152  float elapsedMS;
153  CHECK_STATUS(vpiEventElapsedTimeMillis(evStart, evStop, &elapsedMS));
154  timingsMS.push_back(elapsedMS / AVERAGING_COUNT);
155  }
156 
157  // 4. Performance analysis ----------------------
158 
159  // Get the median of the measurements so that outliers aren't considered.
160  nth_element(timingsMS.begin(), timingsMS.begin() + timingsMS.size() / 2, timingsMS.end());
161  float medianMS = timingsMS[timingsMS.size() / 2];
162 
163  printf("Approximated elapsed time per call on %s: %f ms\n", strBackend.c_str(), medianMS);
164  }
165  catch (std::exception &e)
166  {
167  std::cerr << e.what() << std::endl;
168  retval = 1;
169  }
170 
171  // 4. Clean up -----------------------------------
172 
173  // Destroy stream first, it'll make sure all processing
174  // submitted to it is finished.
175  vpiStreamDestroy(stream);
176 
177  // Now we can destroy other VPI objects, since they aren't being
178  // used anymore.
179  vpiImageDestroy(image);
180  vpiImageDestroy(blurred);
181  vpiEventDestroy(evStart);
182  vpiEventDestroy(evStop);
183 
184  return retval;
185 }
用于处理 VPI 事件的函数和结构。
声明实现高斯滤波器算法的函数。
const char * vpiImageFormatGetName(VPIImageFormat fmt)
返回图像格式的字符串表示形式。
用于处理 VPI 图像的函数和结构。
VPI 状态代码处理函数的声明。
声明处理 VPI 流的函数。
#define VPI_REQUIRE_BACKENDS
要求创建请求的后端。
定义: Types.h:159
#define VPI_EXCLUSIVE_STREAM_ACCESS
指定内存一次只能被一个流访问。
定义: Types.h:136
struct VPIEventImpl * VPIEvent
事件的句柄。
定义: Types.h:244
VPIStatus vpiEventElapsedTimeMillis(VPIEvent start, VPIEvent end, float *msec)
计算两个已完成事件之间经过的时间(以毫秒为单位)。
VPIStatus vpiEventRecord(VPIEvent event, VPIStream stream)
在此调用时捕获事件中流命令队列的内容。
VPIStatus vpiEventCreate(uint64_t flags, VPIEvent *event)
创建事件实例。
VPIStatus vpiEventSync(VPIEvent event)
阻塞调用线程,直到事件发出信号。
void vpiEventDestroy(VPIEvent event)
销毁事件实例以及它拥有的所有资源。
VPIStatus vpiSubmitGaussianFilter(VPIStream stream, uint64_t backend, VPIImage input, VPIImage output, int32_t kernelSizeX, int32_t kernelSizeY, float sigmaX, float sigmaY, VPIBorderExtension border)
在图像上运行 2D 高斯滤波器。
uint64_t VPIImageFormat
预定义的图像格式。
void vpiImageDestroy(VPIImage img)
销毁图像实例。
struct VPIImageImpl * VPIImage
图像的句柄。
定义: Types.h:256
VPIStatus vpiImageCreate(int32_t width, int32_t height, VPIImageFormat fmt, uint64_t flags, VPIImage *img)
使用指定的标志创建空的图像实例。
struct VPIStreamImpl * VPIStream
流的句柄。
定义: Types.h:250
VPIBackend
VPI 后端类型。
定义: Types.h:91
void vpiStreamDestroy(VPIStream stream)
销毁流实例并释放所有硬件资源。
VPIStatus vpiStreamCreate(uint64_t flags, VPIStream *stream)
创建流实例。
@ VPI_BACKEND_CUDA
CUDA 后端。
定义: Types.h:93
@ VPI_BACKEND_PVA
PVA 后端。
定义: Types.h:94
@ VPI_BACKEND_CPU
CPU 后端。
定义: Types.h:92
@ VPI_BORDER_ZERO
图像外部的所有像素均被视为零。
定义: Types.h:278