604cffb5e5
PoC для validation концепта перед инвестированием в Phase 1. Структура: - tools/spike/common.h — типы SharedHeader / SlotDescriptor / NV12 meta - tools/spike/pingpong_producer.cu — аллоцирует CUDA pool, экспортирует IPC handles в /dev/shm/cuframes-spike-<key>, имитирует publish frames с monotonic pattern - tools/spike/pingpong_consumer.cu — открывает handles, читает frames, verify содержимого (no torn frames), измеряет latency, печатает summary - tools/spike/CMakeLists.txt — sm_75/86/89/90/120 для RTX 5090 - tools/spike/bench.sh — basic / multi-consumer / stress scenarios - tools/spike/README.md — what / how / acceptance Намеренные упрощения PoC (не идём в Phase 1 пока без validation): - 2-slot ring (Phase 1 будет N) - POSIX shared memory + atomic seq (без Unix socket handshake) - cudaStreamSynchronize sync (Phase 0 spike проверит будет ли достаточно; альтернатива cudaIpcEventHandle_t — отложена) - NV12 hardcoded (других форматов в Phase 1) - Drop-oldest backpressure (без ACK protocol) Acceptance Phase 0: - p99 latency на RTX 5090 для FullHD < 5 ms - throughput ≥ 1 GB/s - multi-consumer (3) с сопоставимой latency - cross-container работает - 1-hour stress без VRAM/RAM leak Если acceptance fail → дизайн пересмотр (sync через CUDA IPC events).
95 lines
3.7 KiB
Markdown
95 lines
3.7 KiB
Markdown
# Phase 0 — CUDA IPC ping-pong spike
|
||
|
||
PoC для измерения latency / throughput / multi-consumer behavior **без**
|
||
FFmpeg / Frigate / сложности. Чистый CUDA IPC между двумя процессами.
|
||
|
||
## Что проверяет
|
||
|
||
1. **End-to-end latency**: producer → write CUDA frame → consumer reads → ACK.
|
||
2. **Throughput**: сколько FullHD-frames/sec можно протолкнуть.
|
||
3. **Multi-consumer**: 1 producer × N consumers (zero-copy для каждого).
|
||
4. **Cross-container**: producer и consumer в разных Docker-контейнерах.
|
||
5. **Crash recovery**: kill consumer / producer → reconnect.
|
||
6. **Memory leak**: 24-hour run без VRAM-роста.
|
||
|
||
## Структура
|
||
|
||
```
|
||
tools/spike/
|
||
├── README.md (этот файл)
|
||
├── CMakeLists.txt стандартный CMake target
|
||
├── common.h shared structs/constants
|
||
├── pingpong_producer.cu producer-процесс
|
||
├── pingpong_consumer.cu consumer-процесс
|
||
└── bench.sh скрипт-обвязка с измерениями
|
||
```
|
||
|
||
## Сборка (внутри dev-контейнера)
|
||
|
||
```bash
|
||
docker compose -f docker/docker-compose.dev.yml exec dev bash
|
||
# Внутри:
|
||
cd /workspace
|
||
cmake -B build -S tools/spike -G Ninja
|
||
cmake --build build
|
||
```
|
||
|
||
Артефакты: `build/pingpong_producer`, `build/pingpong_consumer`.
|
||
|
||
## Запуск (single-host single-container, базовый случай)
|
||
|
||
Терминал 1 (producer — пишет FullHD-frames пока не нажмёшь Ctrl-C):
|
||
```bash
|
||
docker compose -f docker/docker-compose.dev.yml exec dev \
|
||
./build/pingpong_producer --key cam_test --width 1920 --height 1080 --fps 30
|
||
```
|
||
|
||
Терминал 2 (consumer — читает и логирует latency):
|
||
```bash
|
||
docker compose -f docker/docker-compose.dev.yml exec dev \
|
||
./build/pingpong_consumer --key cam_test --count 1000
|
||
```
|
||
|
||
После 1000 кадров consumer печатает summary:
|
||
|
||
```
|
||
=== cuframes spike summary ===
|
||
frames received: 1000
|
||
duration: 33.34 s
|
||
fps: 30.00
|
||
latency mean: 0.42 ms
|
||
latency p50: 0.39 ms
|
||
latency p99: 0.91 ms
|
||
latency max: 3.12 ms
|
||
producer→consumer: zero-copy ✓
|
||
```
|
||
|
||
## Запуск (cross-container — Phase 0 critical test)
|
||
|
||
См. `bench.sh` — поднимает producer в одном контейнере, consumer в другом,
|
||
оба используют `ipc: shareable` namespace.
|
||
|
||
## Acceptance criteria для Phase 0
|
||
|
||
- [ ] Базовый ping-pong работает (1×1)
|
||
- [ ] Multi-consumer (3 consumers, latency сопоставимая)
|
||
- [ ] Cross-container (producer container A → consumer container B)
|
||
- [ ] 1-hour stress без VRAM/RAM leak
|
||
- [ ] Замерено: p99 latency на RTX 5090 для FullHD-frame
|
||
- [ ] Документ `docs/benchmarks-phase0.md` с numbers
|
||
|
||
Если latency p99 > 5 ms или throughput < 1 GB/s → **остановиться** и
|
||
переосмыслить дизайн (возможно CUDA IPC sync через `cudaIpcEventHandle_t`
|
||
вместо `cudaStreamSynchronize`).
|
||
|
||
## Что **не** делает spike
|
||
|
||
- Не использует FFmpeg (это Phase 2)
|
||
- Не имеет ring buffer (это Phase 1; spike использует двойной буфер)
|
||
- Не использует Unix sockets для handshake (handle передаётся через
|
||
файл `/tmp/cuframes-spike-<key>.handle`)
|
||
- Не делает proper backpressure (drop-oldest hardcoded)
|
||
|
||
Spike нарочно **минималистичен** — единственная цель замерить numbers
|
||
и провалидировать concept перед инвестированием в Phase 1.
|