OpenCV TickMeter vs External Profiler for Primitive-Level Bottleneck Attribution
0 reputation · 28 Jun 2021, 05:41 UTC
0 reputation · 28 Jun 2021, 05:41 UTC
When profiling an OpenCV image-processing pipeline — decode, resize, color conversion, inference — the documented approach wraps each stage in cv::TickMeter (C++) or cv2.getTickCount() (Python) and runs many iterations after a warm-up pass. This yields reliable per-stage wall times, but it cannot break down time spent inside a single optimized primitive such as resize or GaussianBlur when those functions dispatch to SIMD, OpenCL, or multithreaded backends.
The unresolved decision is whether wall-time measurements alone are sufficient for optimization prioritization, or whether an external profiler (Linux perf, Intel VTune, py-spy) must be combined with TickMeter to attribute cycles inside a primitive. Constraints include: debug vs release build disparity, cv::setNumThreads() and OPENCV_NUM_THREADS altering parallel behavior, first-call warm-up skewing single-run results, and UMat/OpenCL host-device transfer overhead that may shift rather than remove cost.
Specific questions:
TickMeter time, does adding an external profiler change the optimization target in practice?35275 reputation · 28 Jun 2021, 13:49 UTC
Per-stage wall time from cv::TickMeter is sufficient for optimization prioritization in most pipelines. Add an external profiler only when the dominant stage is a single optimized primitive whose cost you cannot reduce by changing inputs — because at that point the question shifts from which stage to why inside that stage, and TickMeter cannot answer that.
Yes, but only in a specific situation. TickMeter tells you resize takes 60% of the pipeline. What it cannot tell you is whether those cycles are in a SIMD kernel, in parallel_for_ thread synchronization, or in memory-bandwidth-bound loads. Each cause implies a different fix:
cv::setNumThreads() rather than the algorithm.memcpy/allocation paths → the cost is data movement, not compute; reuse buffers or avoid copies.So the profiler changes the target when the dominant primitive's cost is internal. If TickMeter instead shows the time is spread across replaceable stages, wall time alone is actionable and a profiler adds little.
setNumThreads(1) vs default.perf record/perf report (or VTune) to see where cycles land inside the primitive.In short: wall time picks the stage; a profiler picks the lever. Skip step 3 whenever step 2 already gives you a win.
On CPU-only builds, sampling profilers work as expected — but fix the thread count first, because OPENCV_NUM_THREADS changes both wall time and the profile shape, and samplers aggregate across worker threads. Profile at your production thread setting, and confirm symbols resolve; stripped OpenCV binaries show hot spots as opaque library addresses, which limits attribution.
On OpenCL/UMat builds, CPU sampling is misleading in two ways. First, asynchronous execution means a stage can look cheap because the cost surfaces later at a blocking call — synchronize before stopping the timer. Second, host-device transfers and kernel compilation appear as waits or gaps, not CPU samples. Use OpenCL queue profiling or a GPU-aware profiler (e.g., VTune GPU offload) to separate kernel execution from transfer time, and discard first-call iterations, which include kernel compilation.
For Python pipelines, note that py-spy sees Python frames only; time inside native OpenCV calls collapses into the calling frame. Pair it with perf for native attribution.
Always profile the release/optimized build — debug builds have different hot spots and can be orders of magnitude slower. Run enough iterations to average out frequency scaling, and pin CPU frequency if you are comparing configurations. These behaviors are well established across OpenCV 4.x, but verify symbol resolution and OpenCL queue behavior against your specific build.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.