Benchmark — UAV Vision Analytics#
Scope: This benchmarking guide applies to Standalone Mode (pymavlink) only. It does not cover the UAV Mission Compute SDK mode.
This document explains how to measure the performance of the UAV Vision Analytics
application using the calc_stream_density.sh benchmarking script. The script
determines the maximum number of concurrent drone-camera video streams the
system can process (stream density) while sustaining a target frame rate,
and simultaneously collects hardware utilization and power metrics from metrics-manager.
Note: Other benchmarking optios are also offered by Open Edge Platform. Use Visual Pipeline and Platform Evaluation Tool (ViPPET) for comparing AI pipeline performance across CPU, GPU, and NPU.
For platform-level benchmarking, see the Edge Workloads and Benchmarks Guide - a solution for end-to-end video analytics pipelines, vision AI inference, hardware-accelerated media processing, and generative AI.
Table of Contents#
Prerequisites#
Before running the benchmark, ensure the application stack is configured and running. See index.md for full setup instructions.
1. Model must be exported#
The model must exist at:
resources/models/yolov8n-visdrone/best_openvino_model/best.xml
Run make model if it is missing (Deployment will fail with an error if the model is absent).
2. Start the application stack#
The DL Streamer Pipeline Server (dlstreamer-pipeline-server) and
metrics-manager must be running. Use the pymavlink stack:
cd edge-ai-suites/federal-and-aerospace-ai-suite/uav-vision-analytics
make pymav-up
Wait until all containers are healthy:
docker ps --format "table {{.Names}}\t{{.Status}}"
Expected services: dlstreamer-pipeline-server, broker, mavlink-router, px4, metrics-manager.
3. Install required host tools#
Tool |
Used for |
Install |
|---|---|---|
|
DLSPS API calls, metrics polling |
|
|
FPS and HW metrics statistical aggregation |
|
|
Continuous SSE metrics streamer |
|
|
JSON parsing of DLSPS status responses |
|
|
Creating looped video files (optional) |
|
4. Verify services are reachable#
Service |
URL |
Description |
|---|---|---|
DL Streamer Pipeline Server |
|
Pipeline REST API |
metrics-manager |
|
HW metrics SSE + REST endpoint |
curl -s http://localhost:8081/pipelines/status | head -3
curl -s http://localhost:9090/api/v1/metrics/latest | head -3
How the Script Works#
The benchmarking script (benchmark/calc_stream_density.sh) automates three tasks:
Start N concurrent pipeline instances via the DLSPS REST API (
POST /pipelines/user_defined_pipelines/<name>), each with a unique RTSP path, metadata topic, andmodel-instance-idso concurrent streams do not conflict.Collect FPS samples by polling
/pipelines/statusevery second during a configurable measurement window (default 60 s), then computep90/avg/median/minstatistics withgawk.Collect HW metrics from
metrics-managerin parallel via a Python3 SSE streamer, and aggregate avg/min/max per metric for the same measurement window.
Exponential + Bisect Algorithm#
The script finds the maximum sustainable stream count automatically — no manual bounds need to be supplied:
Phase 1 — Exponential doubling:
Test N = 1 → 2 → 4 → 8 → 16 ... until fps/stream drops below the floor (-t)
or N reaches the upper limit (-u, default 24).
Phase 2 — Bisect:
Binary-search between last-passing N (lo) and first-failing N (hi)
until hi - lo <= 1. lo is the max sustainable stream count.
FPS Statistics (p90)#
During each N-stream test, DLSPS reports the avg_fps for every running
pipeline instance every second. After the measurement window ends
(-i, default 60 s), gawk computes:
Metric |
Meaning |
|---|---|
|
p90 FPS of stream N over the window |
|
Median of the per-stream p90 values |
|
Mean of the per-stream p90 values |
|
Standard deviation of per-stream p90 values |
|
Sum of all per-stream p90 values (total system FPS) |
|
Lowest per-stream p90 — used to decide pass/fail vs |
The p90 (90th percentile) is used instead of the raw average to discard
outlier frames caused by pipeline startup spikes or scheduling jitter.
A run passes if throughput min >= target_fps (-t).
HW Metrics Integration#
The script integrates with intel/metrics-manager to collect real hardware
metrics in parallel with FPS sampling.
Collection method — SSE primary, REST fallback:
SSE primary (
GET /metrics/stream)A Python3 subprocess subscribes to the Server-Sent Events endpoint, which streams
data:events as fast as the hardware counters update. All events are written continuously tohw_samples.log— zero polling lag, no missed samples.REST fallback (
GET /api/v1/metrics/latest)
Used automatically if the SSE endpoint is unreachable. Polls at
METRICS_INTERVAL seconds (default: 2 s).
Timing — warmup exclusion:
The HW monitor starts after all pipeline instances reach RUNNING state
(after model loading and JIT compilation finish), and stops before pipeline
teardown. This ensures GPU/NPU warmup time does not skew power and
utilization measurements.
Metrics collected:
Category |
Metric names in |
Notes |
|---|---|---|
CPU |
|
|
GPU engines |
|
Per GPU 0 only |
GPU combined |
|
|
GPU |
|
qmassa-sourced |
Platform power |
|
RAPL + qmassa |
NPU |
|
Zero when pipeline uses CPU/GPU |
Note: HW metrics disabled automatically* if
metrics-manageris not reachable — the FPS benchmark continues normally andhw_sample_count: 0appears inkpi.txt.
Available Pipelines#
Pipeline names are defined in benchmark/benchmark_app_payload.json. Each entry
maps a pipeline name to the DLSPS POST payload (source URI, destination,
inference device, model path).
File-source pipelines (recommended for benchmarking)#
These use uav_sample.avi as a looping file source — ideal for repeatable,
controlled benchmarks with deterministic input:
Pipeline name |
Device |
Source |
|---|---|---|
|
CPU |
|
|
GPU |
|
|
NPU |
|
RealSense camera pipelines#
These use a live Intel RealSense D-series camera (/dev/video4). The camera
must be physically attached and accessible inside the container.
Pipeline name |
Device |
Source |
|---|---|---|
|
CPU |
RealSense (v4l2) |
|
GPU |
RealSense (v4l2) |
|
NPU |
RealSense (v4l2) |
All pipelines use the YOLOv8n-VisDrone model (FP16 OpenVINO IR) at 640×640 resolution for drone object detection (pedestrian, car, van, truck, bus, bicycle, motor, etc.).
List pipeline names available in the payload file at any time:
jq -r '.[].pipeline' benchmark/benchmark_app_payload.json
Run Modes#
All examples assume you run from the app root directory:
cd edge-ai-suites/federal-and-aerospace-ai-suite/uav-vision-analytics
Mode 1 — Single-Pipeline Stream Density#
Finds the maximum number of concurrent streams for a single pipeline while sustaining the target FPS (Uses the exponential + bisect algorithm automatically).
./benchmark/calc_stream_density.sh \
-p uav_object_detection_gpu \
-t 20 \
-i 60
What happens:
Pre-flight check: verifies DLSPS (
http://localhost:8081) and metrics-manager (http://localhost:9090) are reachable.Stops any previously running pipelines.
Tests N=1 → 2 → 4 → 8 … (exponential), then bisects to find the exact max.
At each N: starts streams, waits for RUNNING, collects FPS + HW metrics for 60 s, stops streams.
Prints final result to terminal and writes
benchmark-density-uav_object_detection_gpu/kpi.txt.
Example terminal output:
>>>>> Performing pre-flight checks...
DLSPS is reachable.
HW metrics: http://localhost:9090
>>>>> Attempting to stop all running pipelines.
No running pipelines found.
>>>>> Single-pipeline density search: uav_object_detection_gpu
FPS floor=20 window=60s max_streams=24
>>>>> Density search (exp+bisect): uav_object_detection_gpu
floor=20 fps max=24 streams window=60s
>>>>> [density] N=1 → 24.0 fps/stream (floor=20) — ✓
>>>>> [density] N=2 → 24.0 fps/stream (floor=20) — ✓
>>>>> [density] N=4 → 23.9 fps/stream (floor=20) — ✓
>>>>> [density] N=8 → 11.2 fps/stream (floor=20) — ✗
>>>>> [density] N=6 → 23.8 fps/stream (floor=20) — ✓
>>>>> [density] N=7 → 15.4 fps/stream (floor=20) — ✗
>>>>> Density result: max sustainable = 6 streams @ 23.8 fps/stream
======================================================
✅ FINAL RESULT: Stream-Density Benchmark Completed!
Pipeline : uav_object_detection_gpu
Max streams : 6
fps/stream : 23.8
FPS floor : 20
CPU util : 32.5 %
GPU util : 87.4 %
NPU util : 0.0 %
Pkg power : 28.3 W
======================================================
stream density: 6
Mode 2 — All-Devices Stream Density#
Runs the density search sequentially for multiple pipelines (typically CPU, GPU, NPU) and prints a unified summary table. This is the standard way to generate platform capability claims.
./benchmark/calc_stream_density.sh \
--all-devices \
-p uav_object_detection_cpu uav_object_detection_gpu uav_object_detection_npu \
-t 20 \
-i 60 \
-u 24
What happens:
Pre-flight checks (DLSPS + metrics-manager).
Runs the density search for each pipeline in order: CPU → GPU → NPU.
10-second thermal cooldown between pipeline types.
Prints a unified results table to the terminal.
Example terminal output:
================================================================
UAV VISION ANALYTICS — SUSTAINED STREAM DENSITY RESULTS
FPS floor : 20 Window: 60s Percentile: p90
================================================================
Pipeline Streams FPS@N CPU% GPU% NPU% PkgPwr(W)
------------------------------------------------------------------------
uav_object_detection_cpu 3 20.7476 71.200 0.000 0.000 24.922
uav_object_detection_gpu 4 23.9829 32.510 87.400 0.000 9.627
uav_object_detection_npu 3 23.8675 8.100 0.000 94.300 4.255
================================================================
KPI files:
CPU: benchmark-density-uav_object_detection_cpu/kpi.txt
GPU: benchmark-density-uav_object_detection_gpu/kpi.txt
NPU: benchmark-density-uav_object_detection_npu/kpi.txt
Reading the table:
Streams — maximum concurrent streams sustaining ≥ target FPS (
-t).FPS@N — p90 fps/stream at the max sustainable N.
CPU% — average CPU utilization during the sustained measurement window.
GPU% — average combined GPU utilization (max of CCS and VCS engines per sample).
NPU% — average NPU utilization (non-zero only for NPU pipelines).
PkgPwr(W) — average SoC package power during the measurement window.
Mode 3 — Fixed Stream Count (nstreams)#
Runs a fixed, pre-specified number of streams per pipeline simultaneously (no binary search). Use this to validate a known configuration or benchmark heterogeneous concurrent workloads (e.g., CPU + GPU + NPU running at the same time).
# Run 3 GPU streams and 3 NPU streams simultaneously
./benchmark/calc_stream_density.sh \
-p uav_object_detection_gpu uav_object_detection_npu \
-nstreams 3 3 \
-t 20 \
-i 60
The order of
-nstreamsvalues must match the order of-ppipeline names.
More examples:
# Single pipeline, confirm a specific stream count (e.g., validate GPU=4 claim)
./benchmark/calc_stream_density.sh \
-p uav_object_detection_gpu \
-nstreams 4 \
-i 60
# Three devices concurrently — heterogeneous mixed load
./benchmark/calc_stream_density.sh \
-p uav_object_detection_cpu uav_object_detection_gpu uav_object_detection_npu \
-nstreams 3 4 3 \
-i 60
Terminal summary table (nstreams mode):
================================================================
NSTREAMS RESULTS (p90 window=60s)
================================================================
Pipeline Streams FPS/s CPU% GPU% NPU% PkgPwr(W) GpuPwr(W)
--------------------------------------------------------------------------
uav_object_detection_gpu 3 23.98 32.1 85.2 0.0 9.500 5.210
uav_object_detection_npu 3 23.87 8.0 0.0 94.1 4.100 0.009
--------------------------------------------------------------------------
Total FPS: 143.7 Samples: 29 CPU temp: 62.0°C
KPI: benchmark-multi/kpi.txt
================================================================
CLI Reference#
Usage (stream-density — single pipeline):
./benchmark/calc_stream_density.sh -p <pipeline_name> [options]
Usage (all-devices — sequential density, unified table):
./benchmark/calc_stream_density.sh --all-devices \
-p <cpu_pipeline> <gpu_pipeline> <npu_pipeline> [options]
Usage (nstreams — fixed concurrent streams):
./benchmark/calc_stream_density.sh \
-p <p1> [p2 ...] -nstreams <N1> [N2 ...] [options]
Arguments#
Flag |
Default |
Description |
|---|---|---|
|
required |
Pipeline name(s) from |
|
off |
Run density search sequentially for all |
|
— |
Fixed stream counts per pipeline (nstreams mode). Count order must match |
|
|
FPS floor. A stream count passes only if |
|
|
Measurement window. Seconds to collect FPS + HW metrics at each tested N. Longer = more stable. |
|
|
Upper bound for exp+bisect search. Search stops if N reaches this and still passes. |
|
|
Accepted for compatibility; ignored — exp+bisect always starts from N=1. |
|
|
Throughput percentile for KPI (0.9 = p90). |
|
off |
Skip metrics-manager collection entirely (faster, FPS-only benchmark). |
|
|
metrics-manager base URL. Only needed if not on |
|
|
REST fallback poll interval. Irrelevant when SSE is available. |
Environment variable overrides#
Variable |
Equivalent flag |
Default |
|---|---|---|
|
|
|
|
|
|
|
— |
|
|
— |
|
Common command examples#
# Fastest check — single pipeline, default FPS floor (14.95), no HW metrics
./benchmark/calc_stream_density.sh \
-p uav_object_detection_gpu \
--no-hw-metrics
# Full 3-device benchmark at 20 fps floor, 60s window
./benchmark/calc_stream_density.sh \
--all-devices \
-p uav_object_detection_cpu uav_object_detection_gpu uav_object_detection_npu \
-t 20 -i 60
# Validate a specific claim: confirm GPU sustains 4 streams at ≥20 fps
./benchmark/calc_stream_density.sh \
-p uav_object_detection_gpu -nstreams 4 -t 20 -i 60
# NPU pipeline with wider search range
./benchmark/calc_stream_density.sh \
-p uav_object_detection_npu -t 20 -u 32 -i 60
# Remote machine (e.g., run benchmark from a different host)
DLSPS_NODE_IP=x.x.x.x ./benchmark/calc_stream_density.sh \
-p uav_object_detection_gpu \
-m http://x.x.x.x:9090 \
-t 20
Understanding the Output#
Terminal Summary#
Each mode prints a formatted summary to the terminal after all runs complete:
Density mode (single pipeline): max streams, fps/stream, CPU%/GPU%/NPU% utilization, package power.
All-devices mode: a table with one row per pipeline — streams, FPS@N, CPU%, GPU%, NPU%, PkgPwr(W).
nstreams mode: a table per pipeline with FPS/stream, utilization columns, total FPS, CPU temperature, and HW sample count.
kpi.txt Format#
Each pipeline run writes a kpi.txt with two sections — FPS statistics (always written) and HW metrics (written when metrics-manager is reachable):
# ── FPS section ──────────────────────────────────────────────────────
throughput #1: 23.983 ← p90 FPS for stream 1
throughput #2: 23.911 ← p90 FPS for stream 2
throughput #3: 23.874 ← p90 FPS for stream 3
throughput #4: 23.806 ← p90 FPS for stream 4
throughput median: 23.947 ← median of per-stream p90 values
throughput average: 23.894 ← mean of per-stream p90 values
throughput stdev: 0.071 ← spread across streams
throughput cumulative: 95.574← total system FPS (sum of all stream p90s)
throughput min: 23.806 ← worst-case stream — used for pass/fail vs -t
# ── HW metrics section ────────────────────────────────────────────────
---hw-metrics---
hw_sample_count: 29 ← number of metric snapshots collected
hw_cpu_util_pct avg: 32.510 ← CPU utilization during window (avg/min/max)
hw_cpu_util_pct min: 18.200
hw_cpu_util_pct max: 51.300
hw_cpu_freq_mhz avg: 2850.000
hw_mem_used_percent avg: 42.100
hw_gpu_compute_util_pct avg: 87.400 ← CCS: OpenVINO AI inference engine
hw_gpu_video_util_pct avg: 22.100 ← VCS: H.264 hardware decode engine
hw_gpu_render_util_pct avg: 0.500 ← RCS: 3D render engine
hw_gpu_enhance_util_pct avg: 0.200 ← VECS: video enhancement engine
hw_gpu_util_combined avg: 87.400 ← max(CCS, VCS) per sample, averaged
hw_gpu_freq_mhz avg: 1950.000
hw_rapl_psys_w avg: 45.200 ← full platform power (CPU + iGPU + DRAM + misc)
hw_rapl_pkg_w avg: 28.300 ← SoC package power (CPU cores + iGPU)
hw_rapl_core_w avg: 20.100 ← CPU cores only
hw_rapl_uncore_w avg: 2.400 ← uncore (LLC, memory controller)
hw_pkg_power_w avg: 9.627 ← qmassa SoC power reading (cross-reference)
hw_gpu_power_w avg: 5.484 ← qmassa GPU power rail
hw_npu_utilization avg: 0.000 ← 0.0 when pipeline runs on CPU or GPU
hw_npu_frequency avg: 0.000
hw_npu_power avg: 0.000
hw_sample_count: 0means metrics-manager was unreachable or returned no data. FPS results are still valid.
Output Directory Structure#
All output is created relative to the directory where you run the command (app root recommended):
uav-vision-analytics/
│
├── benchmark-density-uav_object_detection_cpu/ ← best run for CPU pipeline
│ ├── kpi.txt ← FPS stats + hw_* metrics (avg/min/max)
│ ├── hw_samples.log ← raw HW snapshots (key=value lines, "---" per sample)
│ └── sample.logs ← raw DLSPS /pipelines/status JSON per second
│
├── benchmark-density-uav_object_detection_gpu/ ← best run for GPU pipeline
│ ├── kpi.txt
│ ├── hw_samples.log
│ └── sample.logs
│
├── benchmark-density-uav_object_detection_npu/ ← best run for NPU pipeline
│ ├── kpi.txt
│ ├── hw_samples.log
│ └── sample.logs
│
└── benchmark-multi/ ← nstreams mode output
├── kpi.txt
├── hw_samples.log
└── sample.logs
Note: Intermediate numbered directories (
benchmark-1/,benchmark-2/, etc.) are created during the search and automatically cleaned up once the best result is copied to the namedbenchmark-density-<pipeline>/directory.
Troubleshooting#
For all benchmark-related troubleshooting (missing tools, connectivity issues, result anomalies, GPU/NPU visibility, and power readings), see the Troubleshooting guide.