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
+11 -6
View File
@@ -1,8 +1,8 @@
# Observatory: четыре этапа создания полного real-time Perception-профиля
Дата: 2026-09-01; обновлено 2026-09-02 02:47 МСК. Цель прежняя: переносимые полные профили и снижение задержек всей цепочки. Surface cache reuse подтверждён без изменения outputs; 12 новых source-paced проб дали 128/128 и 0 drops. Единственный latency PASS с cadence 50 ms не выдержал повтора. Экспериментальный unified Triton + native NumPy дал mean 75.03 ms, p95 116.76 ms, p99 137.58 ms — strict gate не закрыт. TensorRT дополнительно не принят по parity: одна ячейка grass→hard_surface изменила NO_GO→ALLOW_candidate. Default остаётся PyTorch. Этап 1 частично выполнен; этапы 2–4 не начаты. Допустимый лабораторный overload не равен real-time PASS; static-obstacle и coarse hard_surface остаются достаточным scope.
Дата: 2026-09-01; обновлено 2026-09-02 09:40 МСК. Цель прежняя: переносимые полные профили и снижение задержек всей цепочки. A/B подтвердил значимый вклад автоматического снижения частот GPU/VRAM. При временно фиксированных штатных частотах полный source-paced граф дал p99 70.46/67.77 ms (TensorRT, два повтора) и 77.02 ms (pinned PyTorch), 128/128 без drops. После возврата auto p99 снова 144.88 ms. Исправлена ничья material votes → unknown/NO_GO. Default остаётся PyTorch; TensorRT numeric/material parity не пройдена. Этап 1 частично выполнен; этапы 2–4 не начаты. Bounded Worker-local latency PASS не равен full-session/network/vehicle qualification; static-obstacle и coarse hard_surface остаются достаточным scope.
Последнее изменение, 2026-09-02 01:5702:47 МСК: `d9ea7c1` убирает повторное группирование online cloud; добавлены NVML, source-clocked layer freshness, FP32 TensorRT-кандидат и точный NumPy preprocess без межпроцессного tensor round trip. На 128 K1-кадрах preprocess tensor совпадает побитово; TensorRT mask — нет (4,009 пикселей, 0.01195%). Полный граф выявил policy-эффект на одной ячейке, поэтому кандидат остаётся unqualified. CUDA Graphs и busy-wait не закрыли p99. Непрерывный component-test на реальном кадре ~2.67 ms против десятков ms source-paced server interval задаёт следующий разрез: GPU/host timeline и clocks, без преждевременного обвинения сети или 24 GiB VRAM. Профиль остаётся observation-only; standalone release и внешний transport не проверены.
Последнее изменение, 2026-09-02 09:1109:40 МСК: `c6c42c2` добавляет fail-closed material voting, bounded numerical/cell attribution и read-only clock telemetry. Установлено различие TF32 arithmetic; на спорном пикселе sigmoid округляет scores до ничьей, а старая cell voting ничья давала ALLOW по порядку IDs. Ни arithmetic, ни model/score/freshness thresholds не менялись. A/B выполнен по явному разрешению владельца, частоты восстановлены после каждого запуска, постоянная host policy не установлена. CPU quota не троттлила, лимиты Docker не расширялись; GPU peak 1.82 GiB для TensorRT и 2.30 GiB для PyTorch. Следующий шаг — воспроизводимый Worker operating envelope и layered-freshness ABI, затем общий streaming runtime; не новая модель и не автономия.
Текущий evidence: [отчёт этапа 1](../experiments/perception/PERCEPTION_STREAM_STAGE1_2026-09-01.md), [ADR 0049](adr/0049-stream-first-perception-profiles.md), [candidate manifest](../config/perception/k1-perception-ddrnet39-rfdetr-tgs-prototype-v1.json). Запрет full-source preload закреплён в новом контракте; старый batch materializer пока не удалён и продуктовый путь не переключён.
@@ -131,6 +131,7 @@ IPC/сети, копирований и оркестрации. Вычислит
- Разделить reusable online algorithms и recorded-only подготовку. Проследить, чем заменяются чтение готового `local-surface.npz`, заранее подготовленные TGS rolling inputs, E47 masks и M4 ledgers. Vendor-registered исходные точки/pose допустимы как вход K1; собственные perception-результаты должны вычисляться текущим запуском.
- Проверить совместимость зависимостей в одном образе: текущий DDRNet использует Python 3.9 / PyTorch 1.13.1 cu117 / super-gradients 3.2.0, RF-DETR — TensorRT 11, TGS — C++. Не объявлять их совместимыми в одном interpreter без проверки. Выбрать минимальную изоляцию внутри одного контейнера либо доказанное преобразование модели с parity check; не менять веса/качество молча ради сборки.
- После согласованного освобождения GPU выполнить короткие baseline компонентов первого профиля последовательно и пилот общего расписания. Разделить decode/preprocess, H2D, inference каждой модели, online geometry/TGS, fusion/policy и доставку результата. Отдельно измерить cold startup и warmed steady state; не начинать с полного старого batch-run.
- Включить в preflight/qualification фактические GPU/VRAM clocks, power state/limit, driver и CPU/RAM quotas. Один image на auto/fixed clocks не имеет одинакового измеренного operating envelope. Host-wide настройки меняются только явно разрешённым owner-scoped механизмом с rollback; контейнеру не выдаётся неограниченная власть над GPU ради воспроизводимости.
- Сохранить существующие EoMT artifacts и точный checkpoint. Его отдельное исследование/упаковка — последующий вариант профиля, не обязательная ветка первого прототипа. Не запускать EoMT во время работ DDRNet-профиля.
- Проверить текущие class/score/box-size/FOV фильтры RF-DETR на требования прототипа, включая близкий крупный объект и маленькое животное; наличие имени класса в модели не доказывает достаточное обнаружение. Не менять пороги без новой версии и сравнительного evidence.
@@ -241,13 +242,16 @@ EoMT — семейство ViT-моделей сегментации изобр
- 2026-09-02 01:5702:47 МСК: surface reuse `d9ea7c1` сохранил functional parity 128/128, surface p99 19.44/21.87 ms. NVML/no-telemetry и cadence 50/100 ms не дали повторяемого latency PASS. Unified Triton с DDRNet/RF-DETR исполняет модели последовательно; NumPy preprocessing точен 128/128, но TensorRT mask отличается на 0.01195% пикселей и меняет одну ячейку grass→hard_surface / NO_GO→ALLOW_candidate. Backend не продвинут в default. Измерены пять whole-graph Triton variants, p99 137.58151.37 ms; CUDA Graph/busy-wait проблему не закрыли. 119 focused tests PASS. Evidence: `.runtime/perception-unified-triton-worker-20260902T0227MSK/manifest.json`, SHA-256 `ed81475e71b7d02af03dbc65ef628065baaec80bf4877b83e80cc6b3d682a330`.
- 2026-09-02 02:47 МСК: pilot containers=0, Triton healthy, два Observatory agent running, local 8000=200, 8765 закрыт. Legacy perception Worker восстановлен в прежнюю HTTP404 restart-loop конфигурацию; Ollama/Frigate exited/restart=no. Нет product cutover/deployment, raw/weights/runtime не добавлены в Git.
- Git checkpoint: `d9ea7c1` — surface cache reuse; `34f13ba` — bounded Triton/cadence/NVML/preprocess diagnostics и regression tests. Документы и выводы фиксируются отдельно; push не выполнялся.
- Этап 1: частично выполнен. Следующие задачи: согласовать TensorRT numeric/material parity до его продвижения, измерить GPU clocks и GPU/host timeline при source-paced паузах, довести выходной layered-freshness ABI и общий runtime; затем application↔Worker streaming transport. Нельзя закрывать whole-path gate по повторению одного tensor или принимать малый pixel mismatch без проверки policy.
- 2026-09-02 09:1109:40 МСК: численная диагностика frame 115 связала большую часть mismatch с TF32, а critical policy flip — с sigmoid ties и ничьей двух ground votes. `c6c42c2` переводит tie в unknown/NO_GO; 210 cell×frame значений на 41 кадре изменились только консервативно. 122 focused tests PASS. Masks/detector/geometry/motion outputs не изменились от этой правки.
- 2026-09-02 09:3109:37 МСК: по явному разрешению владельца A/B stock clock lock (requested SM 2610 / memory 10501 MHz; observed CUDA 2610/10251) без изменения 450 W или Docker limits. Auto B: p95/p99 123.14/138.16 ms; locked C/D: 64.08/70.46 и 63.97/67.77 ms; pinned PyTorch E: 71.80/77.02 ms; auto-restored F: 116.71/144.88 ms. Все шесть новых полных проб 128/128, 0 drops; функциональная parity clock A/B 128/128. Это повторяемый bounded latency PASS при указанном envelope, не whole-product qualification. Evidence: `.runtime/perception-parity-pacing-worker-20260902T0911MSK/manifest.json`, SHA-256 `eac303c218ab391ed5dc2cd8d94be6f1eeecec2effc123da7585135a8b63f2dd`.
- 2026-09-02 09:40 МСК: clock resets успешны, GPU снова auto/P8 210/405 MHz, power limit 450 W; pilot containers=0. Четыре Mission Core сервиса восстановлены в прежнюю конфигурацию, Triton healthy/ready=200; прежние сторонние restart-loop дефекты не исправлялись. Ollama/Frigate exited/restart=no, Mac 8000=200, 8765 закрыт. Raw/weights не в Git, push/deployment не выполнялись.
- Этап 1: частично выполнен. Следующие задачи: закрепить воспроизводимый operating envelope Worker и выходной layered-freshness ABI, продолжить общий runtime. Reference PyTorch остаётся допустимой исходной точкой; TensorRT numeric/material parity требуется до его продвижения, но не блокирует reference-ветку. Затем application↔Worker streaming transport и длинные/разные записи. Нельзя расширять короткий latency PASS до сети/автономии или подменять unavailable sensor evidence свежими данными.
- Этапы 2, 3, 4: не начаты. Продуктовый backend/frontend path, active registry, canonical local 8000 и физический источник не переключались. Старый full-source materializer остаётся legacy; новый streaming transport ещё не реализован.
## Surprises / открытые вопросы
- Малое численное расхождение backend не обязательно семантически безвредно: 0.01195% DDRNet pixel mismatch изменили material/policy одной ячейки. Без ground truth нельзя назвать один backend правильным; без parity нельзя заменить reference автоматически.
- Triton server `compute_infer` interval не тождественен CUDA kernel time. Точный реальный frame в непрерывном trtexec дал mean 2.67 ms, тогда как source-paced Triton — 23.61 ms server interval. Clocks/driver/event scheduling ещё не разделены; смена HTTP на gRPC или покупка GPU пока не являются доказанным исправлением.
- Малое численное расхождение backend не обязательно семантически безвредно. Critical cell flip устранён fail-closed tie rule, но PyTorch vs TensorRT material по-прежнему совпадает 127/128 кадров. Без ground truth и явной numeric acceptance нельзя заменить reference автоматически.
- Автоматический downclock GPU/VRAM — подтверждённый A/B источник существенной части tail latency. CUDA Graph capture был реальным; CPU quota не троттлила. Server intervals всё ещё не являются CUDA kernel trace, а остаточные IPC/CPU/network затраты не исчезли. Две fixed-clock пробы пройдут 125-ms gate, возвращённый auto — нет; квалификация должна описывать условия Worker, а не только image digest.
- Глобальная блокировка очереди противоречит независимости будущих Worker; нужно перенести ownership scope, а не просто оставить `max_concurrency=1` во всех слоях.
- Старые combined-package manifests сохраняют batch-семантику и не исполняют detector/geometry/TGS/threat. Красивый общий viewer использует отдельные сохранённые результаты; это не готовый профиль.
- FPS исходного recording и старые sampling/min-FPS требования математически расходятся. Численные product budgets должны быть закрыты в этапе 1; пользователь не задал их в этом уточнении.
@@ -270,6 +274,7 @@ EoMT — семейство ViT-моделей сегментации изобр
- 2026-09-01, решение владельца: coarse `hard_surface` достаточен в сельской среде, включая тротуары/велодорожки. Следствие: прежние обязательные road-only/fine-class требования отменены; один segmenter DDRNet, без urban autonomy scope. Геометрия и unknown/freshness сохраняют приоритет.
- 2026-09-01, решение владельца: Ollama и Frigate больше не нужны в постоянной нагрузке. Остановить сейчас, убрать автозапуск после reboot, не восстанавливать после benchmark; сохранить данные для ручного использования. Выполнено и проверено по Docker/Compose, без физического reboot системы.
- 2026-09-01, инженерное решение: стартовый replay stride=1, source clock=1×, ≤16 MiB inflight и два pending camera frames; whole-path p95/p99 ≤125 ms остаются preregistered candidate. Перегрузка должна быть видна и проваливать strict gate; нельзя скрыто замедлить источник/сменить stride/подменить слой старым результатом.
- 2026-09-02, решение владельца: разрешены временный A/B stock clock lock с возвратом и расширение Docker resource limits при необходимости. Clock A/B выполнен, все настройки восстановлены; CPU/RAM quotas и power limit не изменялись. Постоянный автозапуск фиксированных частот этим испытанием не вводится.
## Восстановление и остановки
@@ -281,6 +286,6 @@ EoMT — семейство ViT-моделей сегментации изобр
## Outcomes / retrospective
Результат этапа 1 на текущий момент: исполняемые invariants, pinned manifest-кандидат, component baseline и полный граф с bounded causal history/queue и исходным 1× clock. Preroll, direct handoff и surface reuse функционально подтверждены. Добавлены измерения слоёв и экспериментальный unified Triton path; 128-frame NumPy tensor parity точна, но TensorRT material/policy parity не пройдена и default не изменён. Все 12 последних проб zero-drop, стабильного 125 ms gate нет. Source-paced server/GPU gap локализован, причина ещё требует GPU/host timeline. Network end-to-end, несколько записей и standalone packaging остаются непроверенными; прежний cached Triton blob / NVCR403 blocker не перепроверялся. Mounted pilot не заменяет поставляемый образ. Ollama/Frigate выключены, остальные временно остановленные сервисы возвращены в исходное состояние.
Результат этапа 1 на текущий момент: полный causal граф, точный source clock, bounded queues, исправленные preroll/handoff/surface reuse и fail-closed material ties. A/B подтвердил downclock как существенную latency-причину; fixed-clock full graph дважды прошёл короткий 125-ms gate на TensorRT, один раз на pinned PyTorch. Auto-restored контроль снова FAIL. TensorRT arithmetic/material parity не пройдена; default PyTorch не менялся. Operating envelope ещё требуется оформить в управляемый Worker lifecycle, layered output ABI — довести. Network end-to-end, несколько/полная запись и standalone packaging не проверены; cached Triton blob / NVCR403 blocker не перепроверялся. Mounted pilot не заменяет поставляемый образ. Частоты восстановлены, Ollama/Frigate выключены, четыре временно остановленных сервиса возвращены в прежнюю конфигурацию.
После этапа 4 здесь будут перечислены digest самостоятельного полного образа, измеренные статусы и ошибки по recordings, реально проверенные source/hardware/config комбинации, состояние сохранённого EoMT-варианта и оставшиеся physical-live/quality/vehicle-integration ограничения. Готовый Docker и готовая автономия не отождествляются.
@@ -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 не маскируются полученным ускорением.