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).
69 lines
2.7 KiB
C++
69 lines
2.7 KiB
C++
// Phase 0 spike — общие типы / константы для producer и consumer.
|
||
//
|
||
// NB: реальный protocol будет в libcuframes (Phase 1). Здесь — минимум
|
||
// для PoC measurement.
|
||
|
||
#pragma once
|
||
|
||
#include <cuda_runtime.h>
|
||
#include <cstdint>
|
||
#include <cstdio>
|
||
#include <cstring>
|
||
|
||
namespace cuframes_spike {
|
||
|
||
// Slot — 1 frame в shared VRAM. Producer пишет, consumer читает.
|
||
// Используем double-buffering (2 slot'а) — пока consumer читает slot A,
|
||
// producer пишет в slot B. Поможет уменьшить contention в PoC.
|
||
constexpr int RING_SIZE = 2;
|
||
|
||
// Format hardcoded для PoC: NV12 (как выдаёт NVDEC по умолчанию).
|
||
// Y plane + UV plane (subsampled 2x2).
|
||
struct FrameMeta {
|
||
int32_t width;
|
||
int32_t height;
|
||
int32_t pitch_y; // строка Y, выровнен на 256 для CUDA
|
||
int32_t pitch_uv; // строка UV
|
||
int32_t format; // 0 = NV12
|
||
};
|
||
|
||
// Descriptor одного slot'а. Хранится в /tmp/cuframes-spike-<key>.handle
|
||
// — это POSIX shared memory.
|
||
struct SlotDescriptor {
|
||
cudaIpcMemHandle_t handle; // 64 байта от NVIDIA для IPC
|
||
uint64_t producer_seq; // обновляется атомарно при publish
|
||
uint64_t consumer_ack_count; // сколько consumers подтвердили
|
||
int64_t pts_ns; // timestamp когда producer публикнул
|
||
};
|
||
|
||
// Header shared memory file.
|
||
struct SharedHeader {
|
||
uint32_t magic; // 0xCC7C1DCC ("CUFRAMES C0")
|
||
uint32_t version;
|
||
FrameMeta meta;
|
||
SlotDescriptor slots[RING_SIZE];
|
||
uint64_t global_seq; // монотонная последовательность producer
|
||
};
|
||
|
||
constexpr uint32_t CUFRAMES_SPIKE_MAGIC = 0xCC7C1DCCu;
|
||
constexpr uint32_t CUFRAMES_SPIKE_VERSION = 1;
|
||
|
||
// Helper: error check + abort.
|
||
#define CHECK_CUDA(call) do { \
|
||
cudaError_t _err = (call); \
|
||
if (_err != cudaSuccess) { \
|
||
fprintf(stderr, "CUDA error at %s:%d: %s\n", \
|
||
__FILE__, __LINE__, cudaGetErrorString(_err)); \
|
||
std::exit(1); \
|
||
} \
|
||
} while (0)
|
||
|
||
// Получить timestamp в наносекундах (для latency-замеров).
|
||
static inline int64_t now_ns() {
|
||
timespec ts;
|
||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||
return static_cast<int64_t>(ts.tv_sec) * 1000000000LL + ts.tv_nsec;
|
||
}
|
||
|
||
} // namespace cuframes_spike
|