docs(perception): record gpu clock latency proof

This commit is contained in:
DCCONSTRUCTIONS
2026-09-02 09:42:40 +03:00
parent c6c42c29e8
commit 4e633a662c
2 changed files with 130 additions and 6 deletions
@@ -23,6 +23,13 @@ p95 128.75/122.69 ms и p99 151.67/157.10 ms. Это не product cutover,
0.01195% отличающихся пикселей изменили одну ячейку grass→hard_surface и
NO_GO→ALLOW_candidate. Default остаётся pinned PyTorch; подробности в конце.
Последнее дополнение — 2026-09-02 09:40 МСК: причинный A/B показал значимый
вклад автоматического снижения частот GPU/VRAM. При временно фиксированных
штатных частотах полный граф дал p99 70.46/67.77 ms (TensorRT, два повтора)
и 77.02 ms (исходный PyTorch); все 128/128 без drops. После возврата auto
p99 снова 144.88 ms. Исправлена ничья material votes → unknown/NO_GO.
Это bounded Worker-local latency PASS, не qualification продукта/сети/автономии.
## Решение и сделанный объём
Первый профиль зафиксирован как `K1 Perception — DDRNet-39 + RF-DETR + TGS`:
@@ -864,3 +871,115 @@ Ollama/Frigate остаются exited/restart=no. Product registry/defaults,
реальный лишний IPC устранён в экспериментальном Triton path, предел лучше
локализован. Стабильный whole-path latency, TensorRT parity, полный freshness
ABI и standalone/network qualification остаются открытыми.
## Численная диагностика и частоты GPU — 2026-09-02 09:1109:40 МСК
Session: `perception-parity-pacing-worker-20260902T0911MSK`.
Код: `c6c42c2`. Модельные/полные пробы строго последовательны на Worker 006;
Mac выполнял только малые regression tests. Образ остался прежним:
`sha256:664824aa25de1db178f177d67a81b01541a938b479812b9383ddbf03f6b59dbe`.
Новый образ, deployment и product cutover не выполнялись.
### Почему изменилась ячейка 1900
На точном frame 115 pinned PyTorch использует `cudnn.allow_tf32=true`,
`matmul.allow_tf32=false`. TensorRT plan построен с `--noTF32`.
Повтор PyTorch на том же входе точен; default PyTorch vs TensorRT — 42
отличающихся пикселя. Отключение TF32 в диагностическом PyTorch даёт 43
изменения против default, но только **1 пиксель** против TensorRT.
Это не полная backend parity и не причина молча менять reference arithmetic.
Критический пиксель mask `(y=217, x=257)`: raw logits в обоих backend
предпочитают class 50 (grass), но у TensorRT после sigmoid FP32 scores классов
23/50 становятся равны; сохранённый first-index argmax выбирает 23
(hard_surface). У reference scores ещё различаются. Отдельный logits engine
с PyTorch sigmoid/postprocess дал ту же TensorRT mask на этом кадре.
Следовательно, здесь важны и арифметика, и округление sigmoid, а не preprocess.
Удаление sigmoid изменило бы pinned поведение; оно **не выполнено**.
Ячейка grid `(15, 2)` имеет всего два голосующих projected ground points.
Reference: grass 2. TensorRT/no-TF32: grass 1, hard_surface 1. Прежний
`votes.argmax` превращал ничью в hard_surface из-за порядка material IDs.
Исправлено: **любой tie максимума голосов или отсутствие голосов → unknown**,
а unknown остаётся NO_GO. Идентификатор правила в report:
`unique-plurality-or-unknown/v1`. Никакие model/score/freshness пороги не снижены.
Повтор диагностики ячейки и два полных графа подтверждают NO_GO вместо
ALLOW_candidate. В полном candidate run изменились 210 cell×frame material
значений на 41 кадре, исключительно в unknown; при одинаковом stale-state
ни одного ослабления policy. Masks, detections, observations, tracks, threats,
range, occupancy и sensor binding точны 128/128 против старого candidate.
PyTorch vs TensorRT material всё ещё совпадает 127/128 кадров: grass и unknown
не объявляются одинаковым материалом. TensorRT остаётся experimental-unqualified.
### Задержки: GPU не удерживал рабочие частоты
Read-only NVML в полном графе обнаружил переходы SM 2610→450780 MHz,
VRAM 10251→405/810 MHz, P2→P5/P8. При привязке к ближайшей 500-ms sample
DDRNet HTTP mean составлял около 6.52 ms при VRAM 10251 MHz и 41.85 ms
при 405 MHz. Эта привязка сама по себе только корреляция; затем выполнен A/B.
CUDA Graph capture подтверждён verbose log; `cgroup throttled_usec` не рос.
По явному разрешению владельца временно применены `nvidia-smi -i 0 -lgc
2610,2610` и `-lmc 10501,10501`. Фактические CUDA clocks в замерах:
SM 2610 / memory 10251 MHz. Это штатный диапазон, без изменения power limit
450 W, разгона, CPU/RAM quotas, batch/stride/source rate или очередей.
PowerShell `finally` после каждого теста выполнял `-rmc` и `-rgc`.
Везде полный граф DDRNet + RF-DETR + online geometry/distance/motion +
TGS/costmap/policy; 128 исходных кадров, 1×, обе модели на каждом кадре,
GPU строго последовательно, 2 pending / 16 MiB, 0 drops / 0 unaccounted:
| Run | Режим | Mean | p95 | p99, ms | 125-ms gate |
| --- | --- | ---: | ---: | ---: | --- |
| A | TensorRT, auto, tie-safe | 73.42 | 116.63 | 142.34 | FAIL |
| B | TensorRT CUDA Graph + clocks telemetry, auto | 73.66 | 123.14 | 138.16 | FAIL |
| C | Тот же B, fixed clocks | 44.06 | 64.08 | 70.46 | PASS |
| D | Повтор C | 44.37 | 63.97 | 67.77 | PASS |
| E | Pinned PyTorch eager + fixed clocks | 51.08 | 71.80 | 77.02 | PASS |
| F | Тот же B после возврата auto | 70.13 | 116.71 | 144.88 | FAIL |
C/D: DDRNet HTTP mean 6.51/6.71 ms, а не 26.73 ms в B. GPU peak 1863 MiB;
E peak 2359 MiB. C container peak ~1380 MiB при лимите 8192 MiB. CPU quota
8 cores не троттлила; расширять Docker limits ради этого результата не пришлось.
Фиксация частот не изменила candidate functional outputs на всех 128 кадрах;
E сохранил pinned masks и прочие non-material functional outputs 128/128.
Пять коротких component probes не заменяют эту таблицу: повтор одного реального
tensor 32 раза, без DMA mean 2.65 ms; с DMA и паузой 100 ms mean 3.05 ms,
blocking-sync 3.03 ms. Через Triton с теми же паузами wall mean 9.86/10.15 ms
с/без CUDA Graph, server infer ~3.18 ms. Их raw metadata
`transfers_included=false` для Triton было ошибочным описанием: HTTP binary
input/output действительно передавались и учитывались. Метка исправлена в
исходнике; raw evidence не переписывалось.
### Решение, evidence и оставшаяся граница
Снижение частот подтверждено как значимый источник tail latency для данного
Worker/profile. Это не доказательство, что весь runtime оптимален или внешний
network transport несущественен. Постоянный host clock/power policy не установлен.
Следующий runtime должен учитывать operating envelope в Worker preflight и
qualification manifest, под исключительным ownership/lease; Docker не должен
самовольно менять частоты всего хоста. Повторяемые C/D — короткие окна, E —
один reference run; длинная запись, другой recording/hardware, network end-to-end
и field quality не проверены. 76/128 current cloud/pose доступны, 52 unavailable;
в C/D/E соответственно 9/9/10 stale outputs остаются NO_GO, моторы выключены.
Redacted manifest и точные команды/хэши всех 110 локальных evidence artifacts:
`.runtime/perception-parity-pacing-worker-20260902T0911MSK/manifest.json`,
SHA-256 `eac303c218ab391ed5dc2cd8d94be6f1eeecec2effc123da7585135a8b63f2dd`.
Manifest/analysis/final code archive скопированы в одноимённый Worker каталог
`D:/NDC_MISSIONCORE/runtime/experiments/`. Raw source/model outputs не в Git.
Численная 64-MiB logits response разрешена только offline diagnostic, не data plane.
122 focused tests, Ruff и diff-check PASS. Четыре Mission Core сервиса возвращены
в прежнюю конфигурацию; Triton healthy/ready 200. Прежние HTTP404/transport restart
loops других Worker services не исправлялись. Ollama/Frigate exited/restart=no;
pilot containers=0. Финальная GPU: P8, SM 210 / memory 405 MHz, 0% utilization,
power limit 450 W; все clock resets успешны. Canonical Mac 8000=200, 8765 закрыт.
**Статус:** существенная latency-причина найдена, bounded 125-ms gate пройден
при явно указанных условиях даже reference-профилем. Этап 1 остаётся частичным:
layered output ABI и reproducible Worker operating envelope ещё нужно закрепить;
этапы 2–4 не объявляются завершёнными. Отсутствие full-session/standalone/network
proof и TensorRT numeric/material parity не маскируются полученным ускорением.