4862247fe2
Заменяет cudaMalloc + cudaIpcGetMemHandle на cuMemCreate (VMM) +
cuMemExportToShareableHandle(POSIX_FILE_DESCRIPTOR). FDs передаются consumer'у
через sendmsg(SCM_RIGHTS) в handshake. Frigate (s6-overlay не даёт share PID)
и любой другой consumer работают БЕЗ pid namespace share — только volume mount
unix socket'a /run/cuframes и IPC share для /dev/shm header.
Sync: cudaEventRecord+IPC events → cuStreamSynchronize в do_publish.
Producer ждёт ~1 ms что stream flush'нулся, потом atomic_store(seq).
Consumer читает seq через memory_order_acquire и копирует DtoD без
event wait — HW coherence гарантирована на одном GPU.
ABI break (согласован с user'ом):
- magic 0xCC7C1DCC → 0xCC7C1DCE (старые consumers fail cleanly)
- protocol V3 → V4
- libcuframes.so.0 SOVERSION остаётся, но .so.0.3.0 → .so.0.4.0
- EXTERNAL ownership убран (VMM требует cuMemCreate-allocated memory,
нельзя export'нуть произвольный cudaMalloc-pointer как POSIX FD)
- cuframes-rtsp-source переведён на LIBRARY mode + один D2D memcpy
в acquire'нутый slot (overhead малый — публишер всё равно делал такой
D2D из FFmpeg hwframe pool в EXTERNAL pool раньше)
Размер: granularity 2 MB на 5090 → NV12 1920×1080 (~3.1 MB) округляется до
4 MB, +1 MB на slot × 16 × 4 камеры = +64 MB VRAM. Терпимо.
Packet ring (cuframes_packets://) НЕ затронут — отдельный SHM с своим
magic, работает как раньше.
PoC + smoke в spike/:
- vmm_fd_pingpong/ — minimal cuMemCreate+FD round-trip
- smoke_v04/ — full publisher+subscriber, 100/100 frames без pid share
Base image: Dockerfile.runtime → CUDA 12.4 (был 13.0). Matching prod
pipeline + Frigate base, иначе libcudart conflict при load.
Compose stack (localhost-infra repo) — параллельный commit:
- убран pid: container:cuframes-pub-parking из subscribers
- image теги: gx/cuframes:0.4, gx/cuda-grid-pipeline:phase8,
gx/frigate:cuframes-v0.4
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
70 lines
3.0 KiB
Markdown
70 lines
3.0 KiB
Markdown
# vmm_fd_pingpong — spike для cuframes v0.4
|
||
|
||
Проверка: можно ли заменить CUDA IPC mem handles на VMM (cuMemCreate)
|
||
+ POSIX FD export, чтобы убрать требование shared pid/ipc namespaces
|
||
между producer и consumer контейнерами.
|
||
|
||
## Результат: ✅ работает
|
||
|
||
Запуск 2 контейнеров без shared pid/ipc, только volume mount для
|
||
unix-сокета:
|
||
|
||
```
|
||
producer: granularity=2097152
|
||
producer: alloc size=16777216
|
||
producer: exported fd=37 for handle
|
||
producer: listening on /run/spike/pingpong.sock, awaiting consumer...
|
||
|
||
consumer: connected to producer
|
||
consumer: recv fd=38 size=16777216 magic=0xa7
|
||
consumer: imported handle OK
|
||
consumer: mapped + access OK
|
||
consumer: verify mismatch=0/1048576 → ACK=O
|
||
consumer: done (OK)
|
||
```
|
||
|
||
## Ключевые наблюдения
|
||
|
||
- **Granularity на 5090 = 2 MB**. 1920×1080 NV12 (~3.1 MB) округлится до 4 MB.
|
||
16 slots × 4 камеры × +1 MB = +64 MB VRAM поверх текущих cuda IPC аллокаций.
|
||
- **FD передаётся через `sendmsg(SCM_RIGHTS)`** — kernel прокидывает реальный FD
|
||
в receiver namespace, переименовывая в свободный номер. Volume mount unix
|
||
socket'а — единственное требование (`/run/cuframes` уже монтируется как shared).
|
||
- **`cuMemImportFromShareableHandle`** принимает FD как `(void *)(uintptr_t)fd`.
|
||
- **Доступ на consumer side требует `cuMemSetAccess` с правильным `CUmemLocation`** —
|
||
device id из своего `cuDeviceGet`, не наследуется от producer.
|
||
|
||
## Замена events (упрощение этапа C)
|
||
|
||
CUDA events для IPC не имеют POSIX FD path. Внедрять external semaphores
|
||
(OPAQUE_FD) — отдельный API, другая sigal/wait семантика. **Вместо этого:**
|
||
producer вызывает `cuStreamSynchronize(stream)` ПЕРЕД `atomic_store(seq)` в
|
||
`do_publish`. Consumer тогда просто читает seq и копирует DtoD — без event wait.
|
||
|
||
Overhead: ~1 ms на publish × 25 fps = 2.5% CPU time producer'а. Memory
|
||
coherence гарантирована (один GPU, hardware ensures writes visible после
|
||
stream sync).
|
||
|
||
## Сборка
|
||
|
||
```bash
|
||
docker run --rm -v $PWD:/work -w /work nvidia/cuda:12.4.1-devel-ubuntu22.04 \
|
||
bash -c "apt-get install -y build-essential && make"
|
||
```
|
||
|
||
## Запуск теста
|
||
|
||
```bash
|
||
sudo mkdir -p /var/run/spike-pingpong && sudo chmod 777 /var/run/spike-pingpong
|
||
|
||
docker run -d --name spike-prod --runtime=nvidia --gpus all \
|
||
-v $PWD:/work -v /var/run/spike-pingpong:/run/spike \
|
||
nvidia/cuda:12.4.1-base-ubuntu22.04 /work/producer
|
||
|
||
docker run --rm --name spike-cons --runtime=nvidia --gpus all \
|
||
-v $PWD:/work -v /var/run/spike-pingpong:/run/spike \
|
||
nvidia/cuda:12.4.1-base-ubuntu22.04 /work/consumer
|
||
|
||
docker logs spike-prod && docker rm -f spike-prod
|
||
```
|