docs(perception): record decoder parity and remaining full-graph bridge

This commit is contained in:
DCCONSTRUCTIONS
2026-09-02 13:14:11 +03:00
parent f102712f0b
commit a85aed2e03
3 changed files with 167 additions and 5 deletions
@@ -1454,3 +1454,112 @@ camera decoder и causal sensor adapter, соединяющие этот ingress
parity/timing на новом входе. Network/gRPC/authentication, constant controller
root/recovery/inventory, standalone image и product cutover остаются открытыми.
Этап 2 не закрыт, этапы 3–4 не начаты; actuation=false.
## 2026-09-02 13:09 МСК — этап 2, инкремент 4: fMP4 → persistent decoder
Коммиты: `350366b` (общий parser/decoder и synthetic tests), `f102712`
(Worker probes). Это входная часть runtime, **не новый запуск полного графа**.
Никакие модели, thresholds, source clock, GPU clocks или ограничения Docker
полного профиля не менялись. Четыре Mission Core GPU-сервиса не останавливались.
### Реализованная граница
Из `sessions.media` выделен прежний bounded ISO-BMFF timing parser; archive API
сохраняет свои исключения. Новый decoder не импортирует session store/materializer,
не принимает path, frame count, duration или EOF. Принимает init ≤64 KiB и один
полный fMP4 fragment ≤1 MiB. Объявленный subset: H.264, один video track, native
800×600, один sample, явные moof-relative offset/size, без composition offset.
Первый fragment/frame проверяется как random-access/keyframe; DTS непрерывен.
Persistent codec выдаёт ровно один BGR кадр текущего PTS, без flush и будущих
пакетов. B-frames, иной layout/raster, multi-sample, пропуск или неоднозначный
native output завершают decoder; молчаливого fallback нет.
Первый рабочий вариант открывал `init + current fragment` через PyAV на каждом
кадре. Полнофайлового барьера уже не было, но повторялся stream probing.
В принятом варианте bounded parser проверяет размер/offset единственного AVCC
sample и передаёт его непосредственно persistent H.264 codec. NumPy импортируется
до readiness. Только init открывается через PyAV; decode не читает файлов.
### Worker CPU-only A/B и отдельный reference
Эксперименты `perception-stage2-decoder-worker-20260902T1250MSK` и
`perception-stage2-decoder-direct-worker-20260902T1305MSK`: последовательно,
network none, без GPU request, 2 CPU / 512 MiB; process address-space limit 1 GiB.
Исходные 128 fMP4 fragments поступают по одному на исходном 1× времени.
Старая готовая MP4 используется **только** отдельным reference-процессом OpenCV
для сравнения пикселей, не монтируется в новые decoder-прогоны. Reference не
paced, поэтому его throughput не сравнивается с новым source-paced latency.
| Метрика, ms | Первый per-fragment demux | Direct sample / persistent codec |
| --- | ---: | ---: |
| p50 | 10.506 | 5.984 |
| p95 | 19.927 | 11.156 |
| p99 | 20.920 | 18.675 |
| max | 68.909 | 38.066 |
| Первый кадр | 68.909 | 18.675 |
| Codec init, отдельно | 17.854 | 21.103 |
| BGR parity с reference | 128/128 exact | 128/128 exact |
Каждый кадр декодирован до чтения следующего source fragment. Direct peak RSS
59,192 KiB. Это один bounded CPU A/B, не статистическая квалификация всего
профиля и не обещание аналогичного выигрыша при одновременной CPU-части графа.
### Бинарный вход с настоящим декодером
`perception-stage2-decoded-ingress-worker-20260902T1310MSK`: существующий
отдельный source process → IPC v2 → FragmentDecoder в CPU-only probe container
(2 CPU / 768 MiB). Receiver по-прежнему не получает длину записи. Защита raw
headers/reassembly сохранена; init/extradata и decoder scratch резервируются
в общем 16-MiB mailbox **до** использования. Native codec/DPB дополнительно
ограничены container RSS envelope; это не всеобъемлющий Python byte counter.
- 103/103 raw events exact: init 1 + camera 32 + LiDAR 34 + pose 36; timestamp,
sequence, source identity, payload length/SHA сохранены.
- 32/32 decoded BGR exact против независимого reference. Первый decoded callback
за 3.071 s до source End marker; 31/32 до marker, последний дренируется перед
обработкой End получателем. Это нормальная финальная доставка, не EOF barrier.
- Source-due → decoded/hash callback p50/p95/p99/max:
9.257 / 19.317 / 26.019 / 26.019 ms. Decode+hash p95/p99 11.635/19.698 ms.
Эти интервалы не включают модели, geometry, result scene, сеть или browser.
- Wire 4,061,695 bytes; peak accounted input 5,727,751 bytes; остаток 0;
cgroup peak 84,332,544 bytes. Source release lag max 5.838 ms.
- Negative EOF между фрагментами: started=1, completed=0, incomplete=1;
никакого payload callback, terminal=failed, lease/input bytes освобождены.
PyAV18 wheel отсутствовал в исходном diagnostic image. Загружен публичный
`av==18.0.0` (35,536,910 bytes, SHA-256
`ae56b40b6f8b067a8ad2dac664fbfbabac7f7a55b9a7bb031eb99289252bc017`), затем
установлен offline/no-deps в приватный experiment target. Общая система,
Goose Python и image не менялись. Target read-only; его file hashes сохранены
в dependency manifest. Это явный временный mount, не standalone packaging.
246 focused tests PASS; повтор session API + decoder — 55 PASS. Ruff и mypy
трёх source modules PASS; full suite и модели на Mac не запускались.
Все временные probe/dependency containers и source children завершены.
Triton ready=200, Mac 8000=200, 8765 закрыт. Ollama/Frigate exited/restart=no;
GPU auto P8, 982 MiB, power limit 450 W, без изменения частот.
### Evidence и следующий шаг
Принятый manifest: 43 artifacts, SHA-256
`612295eabbc8f203ad9b037a69ac9cf97b2df3134b087d9e51a9786738423f2f`,
`.runtime/perception-stage2-decoded-ingress-worker-20260902T1310MSK/manifest.json`.
Связанные sealed manifests: demux/reference/dependency —
`6a0090f6e1ec11aba436441afacdc0146902a5f911a57a8f989038764dab5d2f`
(22 artifacts); direct decoder —
`0eb32c2c5823130dec87b4e11d825d984b7ee373453b8b4f8b17a4729d5f8134`
(14 artifacts). Снимки code packs, отчёты, per-frame hashes, timestamps,
команды и dependency inventory сохранены; реальные inputs в Git не добавлены.
**Граница:** cloud/pose пока передаются без изменения, но не связываются здесь
в causal scene; calibration identity у hash/decode probe синтетическая и не
допускает model scene. Нативный decoder ещё требуется изолировать supervised
CPU child внутри полного runtime; добавить bounded RPC, causal sensor cut на
момент camera release и handoff decoded ownership в общий mailbox. После этого
запустить тот же DDRNet/RF-DETR/geometry/motion/TGS/policy граф и проверить
raw parity / timing на новом binary input, без `/source.mp4`.
Последний **полный** p95/p99 остаётся 124.98/136.12 ms, fresh 76/128. Не вычитать
decoder-выигрыш из этих значений и не объявлять real-time qualification.
Network/controller/recovery/inventory, standalone image и product cutover
остаются открытыми. Этап 2 продолжается; этапы 3–4 не начаты; actuation=false.