Files
unchecked-io/ROADMAP.md

161 lines
7.4 KiB
Markdown
Raw Normal View History

# UncheckedIO — Development Roadmap
**Goal:** Production-grade, multi-GPU zero-copy ingestion pipeline for LLM providers.
**Current state:** Working proof-of-concept on CUDA (gpu-zero branch). PostgreSQL→Arrow open source on main.
---
## Phase 1 — Fix Correctness (gpu-zero branch)
The GPU feature works in benchmark conditions but has four bugs that would surface immediately in production. Fix these before anything else.
### 1.1 Dynamic buffer allocation
**Problem:** `max_elements = 4096 * 512` is hardcoded at init time. Modern LLMs have 32K128K context windows. A single request at 32K tokens + any real batch size hits this limit.
**Fix:** Allocate `gpu_buffer` and `host_buffer` per-call based on actual `batch_size * max_seq_len`, or use a growable strategy that reallocates when the current batch exceeds the existing buffer size.
### 1.2 Buffer isolation (silent data corruption bug)
**Problem:** Every call to `encode_batch` overwrites the same `CudaSlice`. The DLPack capsule hands PyTorch a pointer to that same memory. A caller holding a tensor from call N gets its data silently overwritten by call N+1.
**Fix:** Double-buffer (ping-pong between two allocations) or allocate a fresh device buffer per call and transfer ownership to the DLPack capsule's deleter so CUDA frees it when PyTorch releases the tensor.
### 1.3 Thread safety
**Problem:** `encode_batch` takes `&mut self`. The `TokenizerEngine` Python class cannot be called from multiple threads simultaneously. Every production serving engine (vLLM, TGI) is multithreaded.
**Fix:** Wrap `PinnedBatcher` in an `Arc<Mutex<>>` or, better, create a `EnginePool` that holds N instances and checks one out per call. Pool size = CPU core count is a reasonable default.
### 1.4 Variable sequence length + padding
**Problem:** `seq_len = encodings[0].len()` assumes all sequences in the batch have identical length. The `tokenizers` crate doesn't apply padding by default, so real variable-length inputs produce an incorrect 2D tensor shape.
**Fix:** Apply padding to `max(encodings[i].len())` within the batch, fill with the tokenizer's `pad_id`, and use the actual padded length as `seq_len`.
---
## Phase 2 — Production Features
### 2.1 vLLM integration
vLLM is the dominant open-source LLM serving engine. Make `unchecked_io` a drop-in tokenization backend for it.
- Implement the `TokenizerGroup` protocol vLLM expects
- The continuous batching scheduler sends variable-length requests asynchronously — this is where Phase 1 fixes must be solid
- Target: a 3-line config change in vLLM to switch to the UncheckedIO backend
- Deliverable: `examples/vllm_integration.py` showing the swap
### 2.2 Streaming / chunked output
**Current:** The entire batch is tokenized and DMA'd in one blocking call.
**Target:** Yield Arrow RecordBatches or DLPack tensors incrementally as tokens are ready, so the model can begin prefill before the full batch is staged.
### 2.3 Attention mask export
LLM inference requires both `input_ids` and `attention_mask`. Currently only token IDs are exported. Add a second DLPack capsule for the mask, or pack both into a named dict capsule.
### 2.4 Configurable max context
Replace the hardcoded constants with constructor arguments:
```python
engine = TokenizerEngine(
tokenizer_path="tokenizer.json",
max_seq_len=32768,
max_batch_size=64,
pool_size=8
)
```
---
## Phase 3 — Multi-GPU Support
### 3.1 ROCm / HIP (AMD) — highest priority
AMD's MI300X has 192GB memory vs the H100's 80GB and providers are actively evaluating it to escape NVIDIA pricing. HIP is the path of least resistance — function names are nearly 1:1 with CUDA (`hipMemHostRegister`, `hipMemcpyHtoD`, `hipMemHostUnregister`).
- Add `rocm` feature flag in `Cargo.toml`
- Create `src/backends/rocm.rs` implementing the same `GpuBackend` trait
- Use AMD's `hip-sys` or `rocm-sys` Rust crate
- The DLPack export layer stays identical — device_type `10` for ROCm vs `2` for CUDA
**Refactor needed first:** Extract a `GpuBackend` trait from the current CUDA-specific code:
```rust
trait GpuBackend {
fn allocate_pinned(capacity: usize) -> Result<PinnedBuffer>;
fn copy_to_device(src: &[i64], dst: &mut DeviceBuffer) -> Result<()>;
fn device_ptr(buf: &DeviceBuffer) -> u64;
fn dlpack_device_type() -> i32;
}
```
### 3.2 OpenCL (broad coverage)
Covers AMD, Intel, older NVIDIA, Apple (via Metal-OpenCL bridge). Lower peak performance than native CUDA/HIP but maximum hardware coverage. Use the `opencl3` Rust crate.
- Add `opencl` feature flag
- Useful for providers running mixed-GPU clusters
### 3.3 Intel Gaudi / Level Zero
Intel's Gaudi 2/3 accelerators are gaining traction for training and are available on AWS and Azure. Intel Level Zero is the low-level API equivalent to CUDA driver API. Lower priority than ROCm but worth tracking.
---
## Phase 4 — Benchmark Suite
The current `benchmark_gpu.py` is good for what it tests but synthetic. A buyer will run their own benchmarks — make it easy and cover real scenarios.
### 4.1 Scenarios to cover
- Short sequences: 128, 256 tokens (BERT-style)
- Medium sequences: 2048, 4096 tokens (typical chat)
- Long sequences: 16K, 32K tokens (document/RAG workloads)
- Variable length batches (realistic distribution, not identical strings)
- Concurrent throughput: simulate N async callers
### 4.2 Tokenizers to cover
- BERT (`bert-base-uncased`) — already done
- LLaMA 3 tokenizer (BPE, more representative of modern LLMs)
- GPT-2 tokenizer (common baseline)
### 4.3 Hardware targets
- Local RTX 3060 (done, 4.7x)
- Cloud H100 SXM (~$2 on Lambda Labs, 30 min run) — **do this first, it changes the pitch**
- AMD MI300X (after Phase 3.1)
### 4.4 What to measure
- Tokens/second throughput
- Latency per batch (p50, p95, p99)
- GPU utilization % (the thing providers care about)
- Memory bandwidth saturation
---
## Phase 5 — Business Infrastructure
### 5.1 Wyoming LLC formation
- Single-member LLC, Wyoming (strongest privacy, no public member disclosure)
- LLC owns all IP
- Registered agent handles public address
- Done before any contract is signed
### 5.2 License structure
- Open core: PostgreSQL→Arrow stays Apache 2.0
- GPU features: commercial license
- Pricing model: annual per-cluster license, priced against GPU utilization savings
- License enforcement: honor-system enterprise contracts initially, not technical DRM
### 5.3 Sales collateral
- 1-page technical brief (problem / mechanism / numbers / what's included)
- Self-contained benchmark the prospect can run on their own hardware
- Reference architecture diagram showing where this sits in a vLLM serving stack
---
## Branch Strategy
| Branch | Purpose |
|--------|---------|
| `main` | Open source PostgreSQL→Arrow, Apache 2.0, public |
| `gpu-zero` | GPU feature development, private |
| `rocm` | AMD port (Phase 3.1) |
| `release/x.y` | Tagged releases for licensing |
The `gpu` feature is compiled in only with `--features gpu`. The open source build never includes GPU code.
---
## Immediate Next Steps (in order)
1. Fix 1.1 (dynamic buffer) — unblocks everything else
2. Fix 1.2 (buffer isolation) — correctness before benchmarking
3. Fix 1.3 (thread safety) — required for any real serving test
4. Fix 1.4 (padding) — correctness
5. Spend $2, run benchmark on cloud H100 — this one number changes the pitch
6. Phase 2.1 vLLM integration — this is the demo that sells the concept
7. Phase 3.1 ROCm — this is the differentiator