The 8 GB Lab Book · Chapter 16
Keep the frame on the GPU side
- Board
- Jetson Orin Nano Super
- Mode
- 15W
- Setup
- GStreamer 1.20
- Summary
- The CPU JPEG path, jpegenc then jpegdec, took 16.95 ms per 720p frame: 51% of the frame budget.
What I expected
Chapter 6 charged 2.27 ms for one u8 crossing plus GPU work. For Prior 1, I budgeted approximately a millisecond for each hop. That is near 3 ms for P2 (convert, cross) and near 2 for P3. For P4 I guessed 10 ms. A software JPEG decoder at approximately 100 Mpx/s (est.) needs 9 ms for 0.92 Mpx.
Prior 2 was that G_cam, which Chapter 1 left as a letter, would be in the tens of MB. That is a small number of RGBA frames in transit.
Prior 3 was that P3 is faster than P2 by one full crossing, two to three ms.
The theory
Bytes per frame, then a two-point line for each path
Bytes per frame (arithmetic)
720p RGBA: 1280 x 720 x 4 B = 3.69 MB 720p I420 source: x 1.5 B = 1.38 MB 480p RGBA: 640 x 480 x 4 = 1.23 MB one 720p crossing at 68 GB/s: 0.054 ms = bus time is nothing; Day 6 again
Two points make a line (720p vs 480p)
pixels: 0.922 M vs 0.307 M, a 3.0x step P2: (5.73 - 1.70) / 0.614 Mpx = 6.6 ns/px fixed: 1.70 - 6.6 x 0.307 = -0.3 ms, so none P4: (16.95 - 8.84) / 0.614 = 13.2 ns/px fixed: 8.84 - 13.2 x 0.307 = 4.8 ms = P2 is pure per-pixel; JPEG has a 4.8 ms handshake
6.6 ns per 4-byte pixel is 0.6 GB/s. This rate suggests a per-pixel pass, not a DMA copy of a buffer. The 4.8 ms fixed part of JPEG is for each frame, and the size does not change it.
The crossing, priced (arithmetic from measured p50)
- P2 - P3, 720p
- 1.04 ms
- bytes not crossing
- 2.46 MB
- crossing rate
- 2.4 GB/s
- bus roof
- 68 GB/s
The setup
python3 ~/day16_nvmm.py
python3 -c "import json;[print(p['name'],p['resolution'],round(p['latency_p50_ms'],2),round(p['latency_p99_ms'],2),round(p['g_cam_mb'],1)) for p in json.load(open('day16/results.json'))['pipelines']]"python3 ~/day16_nvmm.py --device /dev/video0
# MJPEG mode, the line from the script's notes:
# v4l2src device=/dev/video0 io-mode=2 ! image/jpeg,width=1280,height=720,framerate=30/1 ! jpegdec ! video/x-raw,format=I420 ! nvvidconv ! ...
# a UVC camera lands frames in system memory; it cannot hand nvvidconv an NVMM buffer, so P2 is its best case"meta": { "script": "day16_nvmm.py", "res_flag": "both", "warmup": 60, "frames": 600 }
"pipelines": [ ... ]
"notes": [ ... ]The measurement
| What | Expected | Measured | Note |
|---|---|---|---|
| P1 raw system memory (no conversion) @ 720p | - | p50 0.17 / p99 0.67 ms | 30 fps, pool 5 MB |
| P2 NVMM convert, then back to system @ 720p | - | p50 5.73 / p99 6.44 ms | 30 fps, pool 27 MB |
| P3 stay in NVMM, resize there, small buf @ 720p | - | p50 4.69 / p99 5.00 ms | 30 fps, pool 8 MB |
| P4 JPEG encode/decode (the anti-pattern) @ 720p | - | p50 16.95 / p99 19.20 ms | 30 fps, pool 2 MB |
| P1 raw system memory (no conversion) @ 480p | - | p50 0.12 / p99 0.68 ms | 30 fps, pool 1 MB |
| P2 NVMM convert, then back to system @ 480p | - | p50 1.70 / p99 1.84 ms | 30 fps, pool 1 MB |
| P3 stay in NVMM, resize there, small buf @ 480p | - | p50 1.74 / p99 1.87 ms | 30 fps, pool 0 MB |
| P4 JPEG encode/decode (the anti-pattern) @ 480p | - | p50 8.84 / p99 10.35 ms | 30 fps, pool 3 MB |

Memory type on every pad; circle the one crossing
- Source[system]
- NvvidconvRGBA [NVMM]
- Resize480p [NVMM]
- One small buffer[system]
Line at 33.3 ms
| path | p50 ms | p99 ms | pool MB |
|---|---|---|---|
| P1 raw | 0.12 | 0.68 | 1 |
| P2 NVMM, back | 1.70 | 1.84 | 1 |
| P3 NVMM, resize | 1.74 | 1.87 | 0 |
| P4 JPEG | 8.84 | 10.35 | 3 |
Where it broke
For Prior 1, P2 at 5.73 is double my 3 ms. The error is for each pixel, not for each hop. A convert-and-cross costs 6.6 ns for each pixel at all sizes. P4 at 16.95 was near my 10 only by chance. 4.8 ms of it, 28 percent, is a fixed cost, and I had no model for that part. At 480p that fixed part is 54 percent of 8.84.
For Prior 3, the result was 1.04 ms, not 2 to 3. The resize also has a cost, so the saving is a crossing minus a resize. There is a check that this model is correct. At 480p there is nothing to resize, so P3 should be equal to P2. The result is 1.74 against 1.70. The two values agree.
Prior 2 was correct, but only by a small margin. 27 MB / 3.69 MB = 7.3, so the pool of P2 holds seven RGBA frames. I read the 8 MB of P3 as one 720p frame plus three small frames.
I modelled hops again, after Chapter 6 told me to model waits. The two-resolution line is the tool that I should have used first. One subtraction separates the per-pixel cost from the per-frame cost. The price is one extra run.
The source was a test pattern, not a sensor, so 4.69 ms is the cost of the pipeline alone. A C920 adds a USB transfer and a YUY2 unpack or a CPU jpegdec, and none of these four paths contain them.
results.json records the pipeline string of P4: jpegenc, then jpegdec. The two are software elements on the CPU. The 4.8 ms fixed cost is therefore CPU software, not a hardware block. P4 also paid for an encode, not only a decode.
Summary
- NVMM is not faster memory. It is the same LPDDR5. With NVMM, there is no CPU color pass and no copy on each frame. P3 is faster than P2 because the resize occurs before the buffer crosses. The buffer that crosses is small.
- The JPEG path is the cost of a "read frames from files" demo at 30 fps. It uses 51 percent of the frame budget for a CPU encode and decode. 4.8 ms of it (28 percent) is a fixed cost for each frame. At 480p the fixed part is 54 percent of 8.84 ms.
- The pool numbers are the G_cam term from Chapter 1, which now has a measured value. They are small: single-digit to low-double-digit MB. The cost of the camera is latency, not RAM.
- A USB UVC camera like the C920 delivers YUY2 or MJPEG to system memory. P2 is its predicted best case. P4 is an upper limit for its MJPEG case, because that case needs only the decode. CSI through Argus can use P3.
Sources
These sources are not mine. Measured numbers came off my own board; specs, estimates and paper figures are labeled as such.
- NVIDIA NvBufSurface API
- Jetson CSI camera guide
- DeepStream buffer management
- GStreamer nvvidconv docs
- USB vs CSI distinction in Jetson camera architecture