| 192 | } |
| 193 | |
| 194 | void printEpilog(std::vector<InferenceTime> const& timings, float walltimeMs, std::vector<float> const& percentiles, |
| 195 | int32_t batchSize, int32_t infStreams, std::ostream& osInfo, std::ostream& osWarning, std::ostream& osVerbose) |
| 196 | { |
| 197 | float const throughput = batchSize * timings.size() / walltimeMs * 1000; |
| 198 | |
| 199 | auto const getLatency = [](InferenceTime const& t) { return t.latency(); }; |
| 200 | auto const latencyResult = getPerformanceResult(timings, getLatency, percentiles); |
| 201 | |
| 202 | auto const getEnqueue = [](InferenceTime const& t) { return t.enq; }; |
| 203 | auto const enqueueResult = getPerformanceResult(timings, getEnqueue, percentiles); |
| 204 | |
| 205 | auto const getH2d = [](InferenceTime const& t) { return t.h2d; }; |
| 206 | auto const h2dResult = getPerformanceResult(timings, getH2d, percentiles); |
| 207 | |
| 208 | auto const getCompute = [](InferenceTime const& t) { return t.compute; }; |
| 209 | auto const gpuComputeResult = getPerformanceResult(timings, getCompute, percentiles); |
| 210 | |
| 211 | auto const getD2h = [](InferenceTime const& t) { return t.d2h; }; |
| 212 | auto const d2hResult = getPerformanceResult(timings, getD2h, percentiles); |
| 213 | |
| 214 | auto const toPerfString = [&](const PerformanceResult& r) { |
| 215 | std::stringstream s; |
| 216 | s << "min = " << r.min << " ms, max = " << r.max << " ms, mean = " << r.mean << " ms, " |
| 217 | << "median = " << r.median << " ms"; |
| 218 | for (int32_t i = 0, n = percentiles.size(); i < n; ++i) |
| 219 | { |
| 220 | s << ", percentile(" << percentiles[i] << "%) = " << r.percentiles[i] << " ms"; |
| 221 | } |
| 222 | return s.str(); |
| 223 | }; |
| 224 | |
| 225 | osInfo << std::endl; |
| 226 | osInfo << "=== Performance summary ===" << std::endl; |
| 227 | osInfo << "Throughput: " << throughput << " qps" << std::endl; |
| 228 | osInfo << "Latency: " << toPerfString(latencyResult) << std::endl; |
| 229 | osInfo << "Enqueue Time: " << toPerfString(enqueueResult) << std::endl; |
| 230 | osInfo << "H2D Latency: " << toPerfString(h2dResult) << std::endl; |
| 231 | osInfo << "GPU Compute Time: " << toPerfString(gpuComputeResult) << std::endl; |
| 232 | osInfo << "D2H Latency: " << toPerfString(d2hResult) << std::endl; |
| 233 | osInfo << "Total Host Walltime: " << walltimeMs / 1000 << " s" << std::endl; |
| 234 | osInfo << "Total GPU Compute Time: " << gpuComputeResult.mean * timings.size() / 1000 << " s" << std::endl; |
| 235 | |
| 236 | // Report warnings if the throughput is bound by other factors than GPU Compute Time. |
| 237 | constexpr float kENQUEUE_BOUND_REPORTING_THRESHOLD{0.8F}; |
| 238 | if (enqueueResult.median > kENQUEUE_BOUND_REPORTING_THRESHOLD * gpuComputeResult.median) |
| 239 | { |
| 240 | osWarning |
| 241 | << "* Throughput may be bound by Enqueue Time rather than GPU Compute and the GPU may be under-utilized." |
| 242 | << std::endl; |
| 243 | osWarning << " If not already in use, --useCudaGraph (utilize CUDA graphs where possible) may increase the " |
| 244 | "throughput." |
| 245 | << std::endl; |
| 246 | } |
| 247 | if (h2dResult.median >= gpuComputeResult.median) |
| 248 | { |
| 249 | osWarning << "* Throughput may be bound by host-to-device transfers for the inputs rather than GPU Compute and " |
| 250 | "the GPU may be under-utilized." |
| 251 | << std::endl; |
no test coverage detected