Files
esh-pfi-infrastructure/docs/pfi/augaman-speed-bench/README.md
T
vh ef64a69e30 feat(augaman): v0.1.3 on esh-ml1 and fv-ml1; after-bench: GPU ~3x faster, CPU mode regressed
Both hosts are rebuilt from tag v0.1.3 (one ONNX session per detector canvas) and
redeployed. pytest -m gpu tests/vision passes 3/3 on each card.

Server-side for one face, same harness as the v0.1.2 baseline:
- esh-ml1 GPU 144 -> 48 ms
- fv-ml1 GPU 75 -> 27 ms
End to end from nh3-dev: 101.6 and 73.8 ms.

CPU mode got slower on every CPU target: fv-ml1 cpuset 0-5 went 152 -> 205 ms
with a face, and the no-face frame roughly doubled. That is well outside the
run-to-run spread. The suspected cause (not measured) is per-session ORT
thread pools spinning. Reported to augaman-dev. Neither deployment uses CPU
mode.

On esh-ml1 the dependency layer missed the build cache and the rootfs touched
90% until the v0.1.2 image was removed. fv-ml1's build hit the cache, and the
exported requirements are identical, so the stack README now says to check
disk before building on esh-ml1.
2026-09-27 01:13:01 -07:00

114 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# augaman speed bench: CPU vs esh-ml1 GPU vs fv-ml1 GPU
Asked by Prime on 2026-09-27. Two passes of the same harness:
**v0.1.2 (before, 0021–0026 PT)** and **v0.1.3 (after, 0106–0112 PT)**. v0.1.3 gives
each detector canvas its own ONNX session, which removes the ~90 ms CUDA
shape-switching cost that comfy-dev found.
## Headline: server-side ms per frame, one face (a) / no face (c)
| target | v0.1.2 | **v0.1.3** | change |
|---|---|---|---|
| esh-ml1 GPU (RTX 2000E) | 143.8 / 136.2 | **48.0 / 37.2** | ~3.0× faster |
| fv-ml1 GPU1 (RTX PRO 6000) | 75.0 / 69.5 | **27.3 / 22.9** | ~2.8× faster |
| fv-ml1 CPU, cpuset 0-5 | 152.4 / 72.3 | **205.1 / 133.2** | ⚠ ~1.35× / 1.8× SLOWER |
| fv-ml1 CPU, cpuset 0-23 | 155.9 / 61.9 | **204.6 / 124.0** | ⚠ SLOWER |
| esh-ml1 CPU, 6 LXC threads | 887.6 / 159.4 | **1105.1 / 395.4** | ⚠ SLOWER |
End to end from nh3-dev, frame (a) p50: esh-ml1 GPU **101.6 ms** (was 196.5) and fv-ml1
GPU **73.8 ms** (was 126.6). The /health floor is ~27 ms for both.
**The GPU gain is real, and CPU mode regressed.** The CPU slowdown sits far outside the
run-to-run spread: fv CPU6 (c) ran 158–166 ms against 111–113 before. It shows on
every CPU target and hits the no-face frame hardest, which points at the detector
sessions. Hypothesis, NOT measured: each of the three ORT sessions now has its own
intra-op thread pool, whose threads spin while another session runs, so the CPU
oversubscribes. The candidate fixes are `session.intra_op.allow_spinning=0` or a shared
global thread pool. Reported to augaman-dev. Neither deployment runs in CPU mode, so
nothing live is affected.
VRAM after the bench (nvidia-smi): esh-ml1 736 MiB (514 on v0.1.2), fv-ml1 1828 MiB
(1264 on v0.1.2). `pytest -m gpu tests/vision` passes 3/3 on both cards on v0.1.3
(three sessions proven: detector@128, detector@640, embedder). Raw data:
`rows-2026-09-27-v0.1.3.json`, and GPU utilisation samples every ~15 s in
`gpu-util-2026-09-27-v0.1.3.log` (mostly 0%, with peaks of 21% on esh-ml1 and 31%
on fv-ml1 GPU 1; fv-ml1 GPU 2 hit 99% once from another seat's traffic).
---
# v0.1.2 baseline detail
## Harness (it is part of the number)
- **Client:** `bench.py` on nh3-dev, sequential. Each request uses a **fresh
connection** (no keep-alive) and is timed around the whole POST.
- **Protocol:** 3 runs. Per (target, frame, run) there are 3 warm-up requests,
then 20 timed ones. Runs are interleaved across targets and frames, so drift
spreads over every condition. Time: 0021–0026 PT, 2026-09-27.
- **Server-side column:** the change in `augaman_pipeline_seconds{op="probe"}`
sum/count over the 20 timed requests. It is decode + detect + embed with no
network or HTTP, and it is **the device comparison**. It is a mean, not a p50.
- **Floor:** a `/health` round trip, same client. It came out 28–34 ms, higher than
augaman-dev's 16 ms because of the fresh connection per request.
- **Frames** (`make_frames.py`): (a) 1920x1080 grey canvas with one face;
(b) `obama_biden_2015.jpg` as-is, 960x1001, two faces; **(c) the same 1080p grey
canvas with no face**, a detector-only control. Every response was checked for
the expected face count, and **0 of 900 were wrong**.
- **Images:** `augaman:0.1.2`, built from the same tag on each host (separate
builds, same source).
- **Targets:**
| target | device | notes |
|---|---|---|
| esh-ml1 GPU | RTX 2000E Ada 16 GB (50 W) | the production instance; shares the card with TEI + vllm-reward |
| fv-ml1 GPU1 | RTX PRO 6000 Blackwell Max-Q | the new second instance; shares GPU 1 with coder, erp and meromero seats |
| esh-ml1 CPU6 | the LXC's 6 host threads | throwaway `AUGAMAN_DEVICE=cpu` container. pve moved the LXC's cpuset between two reads, so it ran unpinned, with an affinity of 6 |
| fv-ml1 CPU6 | EPYC 9254, cpuset 0-5 | throwaway container; physical cores on socket 0 |
| fv-ml1 CPU24 | EPYC 9254, cpuset 0-23 | throwaway container; all physical cores of socket 0 |
GPU utilisation read 0% on fv-ml1 GPU 1 and 0–9% on esh-ml1 across the samples
(0020–0021, 3 samples each), so the co-resident seats were idle.
## Results (ms): median of the 3 run p50s, [min–max of the run p50s]
| target | (a) 1 face, e2e | (b) 2 faces, e2e | (c) no face, e2e | **server (a)** | **server (b)** | **server (c)** |
|---|---|---|---|---|---|---|
| esh-ml1 GPU | 196.5 [195.0–196.6] | 195.1 [194.0–196.1] | 181.0 [177.6–182.9] | **143.8** | **138.7** | **136.2** |
| fv-ml1 GPU1 | 126.6 [121.8–128.3] | 122.7 [122.4–127.9] | 109.2 [108.7–111.0] | **75.0** | **73.9** | **69.5** |
| esh-ml1 CPU6 | 954.6 [792.8–1023.3] | 1325.6 [1306.9–1327.6] | 205.1 [202.1–228.0] | **887.6** | **1253.4** | **159.4** |
| fv-ml1 CPU6 | 203.6 [203.5–204.5] | 222.5 [216.0–236.4] | 111.6 [110.6–112.7] | **152.4** | **179.4** | **72.3** |
| fv-ml1 CPU24 | 208.1 [204.6–221.6] | 223.7 [219.8–237.1] | 101.1 [100.5–113.4] | **155.9** | **167.3** | **61.9** |
The /health floor was 27.6–33.7 ms (median of run p50s) on every target. The raw
rows are in `rows-2026-09-27.json`, and `summarize.py` reproduces this table.
## What it says
- **fv-ml1's Blackwell is ~2× esh-ml1's Ada on v0.1.2:** 75 vs 144 ms server-side
for one face. End to end from nh3-dev it is 127 vs 197 ms.
- **On v0.1.2, a GPU buys little over a good CPU.** 6 EPYC cores do one face in
152 ms, as fast as esh-ml1's GPU (144 ms). With no face, fv-ml1's CPU (62–72 ms)
matches fv-ml1's GPU (70 ms). The GPU pipeline is dominated by something other
than GPU compute, and the cards read 0–9% utilisation while it ran. That is
consistent with the detector's shape-switching cost that v0.1.3 removes.
- **esh-ml1's CPU is not a viable home:** 0.9 s for one face and 1.3 s for two. Its
embedder costs ~730 ms per face, against ~80 ms on 6 EPYC cores. That gap is much
larger than the detector gap (159 vs 72 ms), and its runs were the noisiest
(793–1023 ms), so treat the esh CPU number as indicative. It was also sharing its 6
threads with TEI.
- **More EPYC threads do not help one frame:** CPU24 is roughly equal to CPU6 on faces.
## Controls and sensitivity
- **Repeats:** 3 runs × 20 per cell. The A-vs-A spread is the min–max above.
- **Noise floor:** the GPU cells spread by ≤6 ms across runs, and the fv CPU cells
by ≤20 ms. The esh-ml1 CPU (a) cell spread by 230 ms. Differences smaller than
those spreads are not findings.
- **Positive control (instrument agreement):** augaman-dev measured esh-ml1 (a)
independently at 186 ms with a 16 ms floor. This harness gets 196.5 ms with a
28 ms floor. Minus each floor, 170 vs 169 ms: they agree.
- **Positive control (detects work):** frame (c) is fastest on every target, and
the gap from (a) to (c) is the embedding cost. It is visible on every device.
- **Negative control:** the face count was checked on all 900 responses, and none
were wrong.