TinyServe is an educational C++/CUDA LLM inference runtime for studying transformer decoding, KV caching, quantization, and GPU bottlenecks. It prioritizes correctness, measurement, and reproducibility over production-level speed.
Status: Minimum Strong Version Complete. The repository provides a fixture-validated scalar float32 transformer path, independent reference checks, contiguous KV-cache decoding, weight-only INT8 storage, a minimal localhost server, and one reproducible benchmark entry point. The executable generates deterministic synthetic token IDs; those bytes have no language-quality meaning. Real-model validation and CUDA kernels remain explicitly deferred.
Architecture: text/raw IDs -> tokenizer boundary -> embeddings -> [RMSNorm -> RoPE/GQA attention -> residual -> RMSNorm -> SwiGLU -> residual] -> final RMSNorm -> logits -> greedy/sampling. Cached decode retains each layer's rotated keys and values; the HTTP target serializes requests through one model queue.
Requirements:
- CMake 3.16 or newer
- A C++17 compiler
- Python 3 for fixture generation and converter tests (not required by the runtime when configured with
-DBUILD_TESTING=OFF) - Optional CUDA work requires an existing CUDA compiler/toolkit and a separately validated GPU; nothing is downloaded automatically
From the repository root:
cmake -S . -B build
cmake --build build
ctest --test-dir build --output-on-failureRun the executable without arguments to print its fixture-validation status and usage. For a typical single-configuration generator:
./build/tinyserveFor Visual Studio generators on Windows, the default configuration is commonly under build/Debug/:
.\build\Debug\tinyserve.exeAfter a Release build, reproduce every locally supported benchmark in a timestamped directory:
.\bench\reproduce.ps1 -BuildDir buildUse -Publish only after reviewing the new run; it explicitly replaces canonical result files and regenerates the report/README tables. Linux/macOS users can run ./bench/reproduce.sh --build-dir build. Commands, overwrite safeguards, fixed repeat counts, and artifact validation are documented in the benchmark harness.
To omit benchmarks from a runtime-only build:
cmake -S . -B build -DTINYSERVE_BUILD_BENCHMARKS=OFFruntime/: the C++ runtime and CLItools/: offline conversion and reference-generation toolstests/: CTest-backed testsbench/: reproducible benchmarks and generated resultsdocs/: scope, design notes, and measurement protocolexamples/: future small usage examples
PyTorch and Hugging Face tooling may be used offline for weight conversion and reference generation, but not as TinyServe's inference backend. See scope and the benchmark protocol for the current contract and limitations.
The final generated summary is the benchmark report, with an independent completion audit. The implementation notes walk through one token, the KV cache tradeoff, and the deferred CUDA analysis.
Wave 1 adds a strict line-based model configuration, a versioned binary tensor container, a standard-library-only deterministic fixture converter, a validating C++ loader, and a temporary byte tokenizer. The format and conversion command are documented in weights; the deliberate tokenizer limitation is documented in tokenizer.
These components are validated against a synthetic fixture only. No real model has been converted or loaded, and the temporary tokenizer is not compatible with Qwen or Llama vocabularies.
Wave 2 adds contiguous float32 tensors, RMSNorm, RoPE, stable causal attention with grouped-query head mapping, SwiGLU, residuals, and reusable single-decoder-block composition. Shape and numerical conventions are documented in CPU math. These scalar implementations prioritize clarity and correctness; no speed claim is made.
The test-enabled build creates build/fixtures/dummy.tserve. Run greedy fixture generation with:
./build/tinyserve generate \
--model build/fixtures/dummy.tserve \
--prompt "Hello" \
--max-tokens 4On a Visual Studio multi-configuration build, use the executable under the selected configuration directory, such as .\build\Release\tinyserve.exe, while the generated fixture remains under build\fixtures\.
The CLI also accepts --tokens 65,66, and positive-temperature sampling supports --top-k and --seed. Every run prints validation_status=synthetic_fixture_unverified. See CPU generation for semantics and limitations.
The test suite compares C++ RMSNorm, RoPE, causal masking, attention, layer outputs, final hidden states, and full last-token logits with a committed reference generated by an independent standard-library Python oracle. Run the Wave 4 checks with:
ctest --test-dir build --output-on-failure -R referenceThe fixture process, fixed numerical tolerances, divergence workflow, optional local Hugging Face dump command, and mandatory approval gate before model downloads are documented in correctness. The offline fixture checks pass without PyTorch, Transformers, network access, or model downloads. The real-model path has not been run and no real-model correctness is claimed.
Use --decode-mode no-cache (the default) or --decode-mode kv-cache with the generation CLI. The cache is a contiguous single-sequence float32 allocation, and cached greedy generation is tested against the preserved no-cache path.
| Mode | Median TTFT (ms) | Median decode (ms) | Decode tok/s | Persistent KV bytes |
|---|---|---|---|---|
| no-cache | 0.170179 | 2.492310 | 2808.639375 | 0 |
| kv-cache | 0.183106 | 0.341675 | 20487.305188 | 192 |
For this run, cached decode throughput was 7.294x the no-cache fixture throughput. This is a tiny synthetic fixture result, not real-model performance. Full metadata and raw samples are in KV cache results.
| Precision | File bytes | Median generation (ms) | Tokens/s | Max logit error | RMSE | Greedy match |
|---|---|---|---|---|---|---|
| float32 | 9802 | 0.534743 | 14960.457640 | 0.000000 | 0.000000 | true |
| int8-storage-float32-runtime | 5999 | 0.525867 | 15212.972101 | 0.011267 | 0.005248 | true |
Fixture file reduction: 38.80%. The runtime dequantizes weights at load time; see quantization results.
The tinyserve_server target implements only GET /health and POST /generate from the canonical API. It binds 127.0.0.1, uses a serialized single-model request queue, and is not a production server.
./build/tinyserve_server --model build/fixtures/dummy.tserve --port 8000
curl http://127.0.0.1:8000/health
curl -X POST http://127.0.0.1:8000/generate \
-H "Content-Type: application/json" \
-d '{"prompt":"A","max_new_tokens":2,"temperature":0,"top_k":1,"top_p":1.0}'| Requests | Concurrency | p50 ms | p95 ms | Requests/s | Aggregate tokens/s |
|---|---|---|---|---|---|
| 24 | 4 | 2.009000 | 15.857400 | 719.905692 | 1439.811385 |
Fixture-only localhost result; see server results.
Wave 6 adds -DTINYSERVE_ENABLE_CUDA=ON as an opt-in probe. It skips cleanly and preserves the CPU build when a CUDA compiler or toolkit is unavailable. On this machine, nvcc, nvidia-smi, CUDA_PATH, and the standard toolkit directory were absent, so RMSNorm, RoPE, GEMV/cuBLAS, and runtime integration remain explicitly deferred rather than untested. See CUDA notes for commands, architecture flags, the availability target, and the exact implementation status.
Wave 8 integration is complete. Hugging Face Transformers, llama.cpp, and vLLM comparisons are not run because no equivalent approved real model/tokenizer or validated CUDA environment exists; the fair, approval-gated setup is specified in the external-engine protocol. No real-model correctness or quality, process peak-memory, activation quantization, INT4, CUDA result, paged cache, continuous batching, streaming, or production-serving capability is claimed.
Future work is deliberately ordered: approve and validate a real checkpoint/tokenizer, run equivalent external baselines, optimize measured CPU bottlenecks, then resume CUDA correctness and microbenchmarks on validated hardware. Detailed completion and deferrals are tracked in tinyserve_implementation_progress.md.
MIT. See LICENSE.