# dotLLM
**Repository Path**: wasgod/dotLLM
## Basic Information
- **Project Name**: dotLLM
- **Description**: 从零纯 C#/.NET 10 实现的高性能原生大模型推理引擎,核心区别于 llama.cpp/Python 封装类方案:无第三方库绑定、全链路自研,不依赖外部推理运行时,面向 .NET 生态原生集成大模型推理。支持模型:Llama、Mistral、Phi、Qwen、DeepSeek 等 Transformer 架构,兼容 GGUF、SafeTensors 权重
- **Primary Language**: Unknown
- **License**: GPL-3.0
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2026-09-15
- **Last Updated**: 2026-09-15
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# dotLLM
**High-performance LLM inference engine written natively in C#/.NET**
[](https://github.com/kkokosa/dotLLM/actions/workflows/ci.yml)
[](https://www.gnu.org/licenses/gpl-3.0)
[](https://dotnet.microsoft.com/)
[](https://dotllm.dev/)
[Website](https://dotllm.dev/) · [Documentation](docs/) · [Roadmap](docs/ROADMAP.md) · [Discussions](https://github.com/kkokosa/dotLLM/discussions)
---
## About
dotLLM is a ground-up LLM inference engine for .NET — not a wrapper around llama.cpp or Python libraries. All orchestration, model loading, tokenization, sampling, and CPU compute are implemented in pure C#, with CUDA GPU acceleration via PTX kernels loaded through the CUDA Driver API (no native shared library). It targets transformer-based models (Llama, Mistral, Phi, Qwen, DeepSeek) with SIMD-optimized CPU and CUDA GPU backends.
> **Status**: Phase 6 complete — speculative decoding, paged KV-cache, Native AOT (experimental), and startup warm-up on top of the OpenAI-compatible API server, built-in chat UI, constrained decoding (JSON/schema/regex/grammar), tool calling, and prompt caching. CUDA GPU backend with CPU/GPU hybrid offloading and KV-cache quantization. SIMD-optimized CPU inference with Q4_K_M, chat templates, streaming, multi-threading, NUMA pinning. Supports Llama, Mistral, Phi, Qwen. Phase 7 (diagnostics & interpretability) in progress — logprobs landed. See [Roadmap](#roadmap).
## Key Features
### Performance
- **Zero-GC inference** — unmanaged memory (`NativeMemory.AlignedAlloc`, 64-byte aligned) for all tensor data; no managed heap allocations on the hot path
- **SIMD vectorization** — `TensorPrimitives` + hand-tuned `System.Runtime.Intrinsics` for quantized matmul, RMSNorm, RoPE, softmax
- **Memory-mapped model loading** — GGUF files loaded via `MemoryMappedFile`; OS demand-paging means multi-GB models load in milliseconds
- **Quantized inference** — FP16, Q8_0, Q4_K_M and other GGUF quantization formats; fused scale×int dot-product kernels operating directly on quantized blocks
### Architecture Support
- **Transformer models** — Llama, Mistral, Phi, Qwen, DeepSeek via parameterized `TransformerBlock` and `ModelConfig`
- **Attention mechanisms** — MHA, MQA, GQA via parameterized `ModelConfig`, with `IAttentionStrategy` for kernel selection
- **Position encoding** — RoPE, ALiBi, absolute, none — pluggable via `IPositionEncoding`
- **Composable sampling** — `ISamplerStep` chain: repetition penalty → temperature → top-k → top-p → min-p → categorical sample
### Serving
- **OpenAI-compatible API** — `/v1/chat/completions`, `/v1/completions`, tool calling, streaming via ASP.NET
- **Paged KV-cache** — PagedAttention with block-level allocation, prefix caching, and copy-on-write
- **Speculative decoding** — draft-verify-accept with KV-cache rollback (greedy mode today; non-greedy planned — see issue #121)
- **Structured output** — FSM/PDA-based constrained decoding guaranteeing valid JSON, JSON Schema, regex, and grammar
- **(Planned) Continuous batching** — iteration-level scheduling with preemption and priority queuing — Phase 9, see [Roadmap](#roadmap)
### Extensibility
- **Pluggable backends** — `IBackend` interface with separate packages per backend (CPU, CUDA, ROCm)
- **Diagnostic hooks** — zero-cost `IInferenceHook` points for activation capture, logit lens, SAE integration
- **(Planned) LoRA adapters** — runtime loading, no weight merging, concurrent multi-adapter serving — Phase 7, see [Roadmap](#roadmap)
- **(Planned) OpenTelemetry observability** — `System.Diagnostics.Metrics` + `Activity` for throughput, latency, and per-request tracing — Phase 7 / Phase 9, see [Roadmap](#roadmap)
## Architecture Overview
dotLLM is organized as a layered architecture where each layer depends only on the layers below it:
```
┌─────────────────────────────────────────┐
│ DotLLM.Server │ ASP.NET OpenAI-compatible API
├─────────────────────────────────────────┤
│ DotLLM.Engine │ KV-cache, scheduler, samplers,
│ │ constraints, speculative decoding
├──────────┬──────────┬───────────────────┤
│ DotLLM. │ DotLLM. │ DotLLM.Cpu/Cuda │ GGUF/SafeTensors, BPE/SPM,
│ Models │Tokenizers│ (backends) │ SIMD kernels / CUDA kernels
├──────────┴──────────┴───────────────────┤
│ DotLLM.Core │ Interfaces, tensor types, config
└─────────────────────────────────────────┘
```
Each project ships as a separate NuGet package, so users pull in only what they need. `DotLLM.Core` defines all abstractions (`ITensor`, `IBackend`, `IModel`, `ISamplerStep`, etc.) while concrete implementations live in their respective projects.
## Getting Started
There are two paths: grab a pre-built release and run it, or clone the repo and build from source.
### Use a pre-built release
Pick one of three install options.
**Option A — install as a global .NET tool** (requires .NET 10 runtime):
```bash
dotnet tool install -g DotLLM.Cli --prerelease
# Download a model once, then use it anywhere
dotllm model pull QuantFactory/SmolLM-135M-GGUF
dotllm run QuantFactory/SmolLM-135M-GGUF -p "The capital of France is" -n 64
dotllm serve QuantFactory/SmolLM-135M-GGUF # OpenAI-compatible API + chat UI
```
**Option B — download a self-contained binary** (no .NET install needed — the runtime is bundled):
Grab the archive for your platform from the [latest release](https://github.com/kkokosa/dotLLM/releases/latest):
- Windows x64: `dotllm--win-x64.zip`
- Linux x64: `dotllm--linux-x64.tar.gz`
- macOS (Apple Silicon): `dotllm--osx-arm64.tar.gz`
Unpack and run:
```bash
# Linux / macOS
tar -xzf dotllm--linux-x64.tar.gz
cd dotllm--linux-x64
./dotllm model pull QuantFactory/SmolLM-135M-GGUF
./dotllm run QuantFactory/SmolLM-135M-GGUF -p "The capital of France is" -n 64
./dotllm serve QuantFactory/SmolLM-135M-GGUF # OpenAI-compatible API + chat UI
```
```powershell
# Windows
Expand-Archive dotllm--win-x64.zip -DestinationPath .
cd dotllm--win-x64
.\dotllm.exe model pull QuantFactory/SmolLM-135M-GGUF
.\dotllm.exe run QuantFactory/SmolLM-135M-GGUF -p "The capital of France is" -n 64
```
*Experimental:* Native AOT builds for Linux and Windows are also attached to each release (`dotllm--aot-.{zip,tar.gz}`) — smaller and faster to start, but please [file an issue](https://github.com/kkokosa/dotLLM/issues/new) if you hit a crash.
**Option C — reference the libraries from your .NET app** — see [NuGet Packages](#nuget-packages) below.
### Build from source
Clone the repository and build with the .NET 10 SDK.
**Prerequisites:**
- [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0)
- [Python 3.10+](https://www.python.org/) with `pip install rich InquirerPy` — only needed for the benchmark scripts under `scripts/`
- *Optional:* [llama.cpp](https://github.com/ggerganov/llama.cpp) for comparison benchmarks (see [llama.cpp setup](#llamacpp-setup))
```bash
git clone https://github.com/kkokosa/dotLLM.git
cd dotLLM
dotnet build -c Release
```
When built from source, replace `dotllm ` in the [Usage](#usage) examples below with `dotnet run --project src/DotLLM.Cli -c Release -- `.
## Usage
dotLLM ships a single CLI tool with four command groups:
- **`dotllm model`** — download, list, search, and inspect GGUF models
- **`dotllm run`** — single-shot text generation with a performance summary
- **`dotllm chat`** — interactive multi-turn REPL with chat template formatting
- **`dotllm serve`** — OpenAI-compatible HTTP API with a built-in web chat UI
Models are identified by a local `.gguf` path or a HuggingFace repo ID (e.g., `QuantFactory/SmolLM-135M-GGUF`). **Models must be downloaded explicitly with `dotllm model pull` before they can be used** — `run`, `chat`, and `serve` read from `~/.dotllm/models/` and do not auto-fetch.
### Manage models
```bash
# Search HuggingFace for GGUF repos
dotllm model search llama --limit 5
# Download a repo (streams the .gguf files + tokenizer metadata into ~/.dotllm/models/)
dotllm model pull QuantFactory/SmolLM-135M-GGUF
# List everything cached locally
dotllm model list
# Show architecture, quantizations, and tokenizer info for a cached repo
dotllm model info QuantFactory/SmolLM-135M-GGUF
# Remove a cached repo
dotllm model delete QuantFactory/SmolLM-135M-GGUF
```
### Run — single-shot generation
Encodes a prompt, streams tokens to stdout, and prints a performance + memory summary.
```bash
# Greedy generation (default: temperature=0, max-tokens=128)
dotllm run QuantFactory/SmolLM-135M-GGUF -p "The capital of France is" -n 64
# Sampled generation
dotllm run QuantFactory/SmolLM-135M-GGUF -p "Once upon a time" -n 128 -t 0.7 --top-k 40 --top-p 0.95
# JSON output (for scripting / piping)
dotllm run QuantFactory/SmolLM-135M-GGUF -p "Hello" --json
# Long prompts: read from a file instead of the command line
dotllm run QuantFactory/SmolLM-135M-GGUF --prompt-file prompt.txt -n 256
# Select a specific quantization when a repo has multiple .gguf files
dotllm run QuantFactory/SmolLM-135M-GGUF -p "Test" -q Q8_0
# GPU inference (requires NVIDIA GPU + CUDA Toolkit)
dotllm run QuantFactory/SmolLM-135M-GGUF -p "The capital of France is" --device gpu
# NUMA / P-core aware CPU threading
dotllm run QuantFactory/SmolLM-135M-GGUF -p "Test" --threads 8 --decode-threads 4 --numa-pin
# KV-cache quantization (Q8_0 / Q4_0) to fit longer contexts in memory
dotllm run QuantFactory/SmolLM-135M-GGUF -p "..." --cache-type-k q8_0 --cache-type-v q8_0
# Constrained JSON output
dotllm run QuantFactory/SmolLM-135M-GGUF -p "List 3 colors as JSON." --response-format json_object
# Speculative decoding — target + draft must share the same vocabulary.
# Example pair (validated by scripts/test_models_speculative.py):
# target: Llama-3.2-3B-Instruct Q8_0 draft: Llama-3.2-1B-Instruct Q4_K_M
dotllm model pull bartowski/Llama-3.2-3B-Instruct-GGUF
dotllm model pull bartowski/Llama-3.2-1B-Instruct-GGUF
dotllm run bartowski/Llama-3.2-3B-Instruct-GGUF -q Q8_0 \
-p "Explain what a CPU cache is in one sentence." -n 48 \
--speculative-model bartowski/Llama-3.2-1B-Instruct-GGUF --speculative-k 5
```
Sample output:
```
── dotllm | Llama 30L/576H | Q8_0 | 16 threads | greedy ──────────────────
The capital of France is Paris. Paris is a city of romance and culture,
╭──────────────────────────────────────────────────────────────────────────╮
│ │
│ Generation Complete 163.27 tok/s │
│ │
│ Performance │
│ Prefill 12.3 ms 6 tokens 487.80 tok/s │
│ Decode 91.8 ms 15 tokens 163.40 tok/s │
│ Sampling 0.1 ms 15 tokens │
│ ────────────────────────────────────────────────────────── │
│ Total 104.2 ms 21 tokens 201.54 tok/s │
│ Load 456.7 ms │
│ │
│ Memory │
│ Weights 136.73 MiB (memory-mapped) │
│ Compute 2.25 MiB │
│ KV Cache 158.20 MiB (192 slots) │
│ ────────────────────────────────────────────────────────── │
│ Total 297.18 MiB │
│ │
│ length | 6 prompt, 15 generated │
╰──────────────────────────────────────────────────────────────────────────╯
```
### Chat — interactive REPL
Multi-turn chat with persistent history, using the model's built-in chat template (falling back to ChatML). Prompt caching reuses KV-cache state across turns so subsequent turns skip redundant prefill.
```bash
# Basic chat
dotllm chat QuantFactory/SmolLM-135M-GGUF
# With a system prompt and sampling
dotllm chat QuantFactory/SmolLM-135M-GGUF --system "You are a helpful assistant." -t 0.8 --top-p 0.95
# GPU + KV-cache quantization for long contexts
dotllm chat bartowski/Llama-3.2-3B-Instruct-GGUF --device gpu --cache-type-k q8_0 --cache-type-v q8_0
```
In-session commands: `/exit` or `/quit` to leave, `/clear` to reset history (keeps the system prompt), `/system ` to change the system prompt.
Sample session:
```
── dotllm chat | Llama 30L/576H | Q8_0 | 16 threads | greedy ─────────────
Type /exit to quit, /clear to reset history, /system to set system prompt.
>>> Hello, how are you?
I'm doing well, thank you for asking! How can I help you today?
[42 prompt tokens, 18 generated tokens, 28 ms TTFT, 487.8 prefill tok/s, 163.4 decode tok/s]
>>> What is 2+2?
2 + 2 = 4.
[78 prompt tokens, 12 generated tokens, 45 ms TTFT, 312.5 prefill tok/s, 155.2 decode tok/s]
>>> /clear
History cleared.
>>> /exit
```
### Serve — OpenAI-compatible API + chat UI
Starts a local HTTP server exposing an OpenAI-compatible API (`/v1/chat/completions`, `/v1/completions`, `/v1/models`, `/v1/tokenize`, streaming SSE, tool calling) plus a built-in single-page web chat UI. Paged KV-cache, prompt caching, and startup warm-up are on by default. The browser opens automatically unless `--no-browser` is set.
```bash
# Start the server with a loaded model and open the chat UI
dotllm serve QuantFactory/SmolLM-135M-GGUF
# Bind a public interface, custom port, API-only, no auto-browser
dotllm serve QuantFactory/SmolLM-135M-GGUF --host 0.0.0.0 --port 9000 --no-ui --no-browser
# Start without a model — pick one from the chat UI
dotllm serve
# GPU with partial hybrid offload and more warm-up iterations
dotllm serve bartowski/Llama-3.2-3B-Instruct-GGUF --device gpu --gpu-layers 24 --warmup-iterations 5
# Speculative decoding — draft must share the target's vocabulary
dotllm serve bartowski/Llama-3.2-3B-Instruct-GGUF -q Q8_0 \
--speculative-model bartowski/Llama-3.2-1B-Instruct-GGUF --speculative-k 5
```
Any OpenAI-compatible client works against the running server:
```bash
curl -N http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "SmolLM-135M",
"messages": [{"role":"user","content":"Say hi in one word."}],
"stream": true
}'
```
To embed the same endpoints inside your own ASP.NET Core app, see [Host the OpenAI API in your ASP.NET app](#host-the-openai-api-in-your-aspnet-app) below.
### CLI option reference
**Common options** (shared by `run`, `chat`, and `serve`):
| Option | Short | Default | Description |
|--------|-------|---------|-------------|
| `--device` | `-d` | `cpu` | Compute device: `cpu`, `gpu`, `gpu:0`, `gpu:1` |
| `--gpu-layers` | | *(all if `gpu`, 0 if `cpu`)* | Transformer layers on GPU (hybrid offload) |
| `--threads` | | 0 (auto) | CPU threads for inference |
| `--decode-threads` | | 0 (auto) | Decode threads (capped at memory channels) |
| `--numa-pin` | | false | Pin workers to NUMA-local cores (multi-socket) |
| `--pcore-only` | | false | Pin workers to P-cores only (Intel hybrid) |
| `--quant` | `-q` | *(auto)* | Quant filter when a repo has multiple `.gguf` files (e.g., `Q4_K_M`) |
| `--cache-type-k` | | `f32` | KV-cache key quant: `f32`, `q8_0`, `q4_0` |
| `--cache-type-v` | | `f32` | KV-cache value quant: `f32`, `q8_0`, `q4_0` |
| `--speculative-model` | | *(none)* | Draft model for speculative decoding (must share vocab) |
| `--speculative-k` | | 5 | Draft tokens per speculative step |
**Sampling & constraints** (shared by `run` and `chat`):
| Option | Short | Default (`run`) | Default (`chat`) | Description |
|--------|-------|-----------------|------------------|-------------|
| `--max-tokens` | `-n` | 128 | 512 | Max tokens per generation |
| `--temp` | `-t` | 0 (greedy) | 0 (greedy) | Sampling temperature |
| `--top-k` | | 0 (off) | 0 (off) | Top-K sampling |
| `--top-p` | | 1.0 | 1.0 | Nucleus threshold |
| `--min-p` | | 0 (off) | 0 (off) | Min-P threshold |
| `--repeat-penalty` | | 1.0 | 1.0 | Repetition penalty |
| `--repeat-last-n` | | 0 (full) | 0 (full) | Penalty lookback window |
| `--seed` | `-s` (run only) | *(random)* | *(random)* | Random seed for reproducibility |
| `--cache-window` | | 0 | 0 | Full-precision tail window for KV quant |
| `--paged` | | off | off | Use paged (block-based) KV-cache |
| `--response-format` | | `text` | `text` | `text`, `json_object`, `json_schema`, `regex`, `grammar` |
| `--schema` | | — | — | JSON Schema (or `@file.json`) for `json_schema` |
| `--pattern` | | — | — | Regex pattern for `regex` |
| `--grammar` | | — | — | GBNF grammar (or `@file.gbnf`) for `grammar` |
| `--tools` | | — | — | Tool definitions JSON (or `@file.json`) |
**`run`-only:**
- `--prompt` / `-p` — input prompt (**required** unless `--prompt-file` is given)
- `--prompt-file` — read the prompt from a file instead of `--prompt`. A single trailing newline is stripped. Mutually exclusive with `--prompt`
- `--json` — emit a single JSON result object (suppresses formatted output)
**`chat`-only:**
- `--system` / `-s` — system prompt
- `--system-file` — read the system prompt from a file instead of `--system`. A single trailing newline is stripped. Mutually exclusive with `--system`
- `--tool-choice` — `auto` (default), `none`, `required`, or a function name
- `--no-prompt-cache` — disable KV-cache reuse across turns
- `--prompt-cache-size` — max cached sessions (default: `1`)
- `--verbose` / `-v` — debug output (finish reason, raw text, tool-call details)
**`serve`-only:**
| Option | Short | Default | Description |
|--------|-------|---------|-------------|
| `--host` | | `localhost` | Bind address |
| `--port` | `-p` | `8080` | Port to listen on |
| `--no-ui` | | false | Disable the built-in chat UI (API only) |
| `--no-browser` | | false | Don't auto-open the browser |
| `--no-paged` | | false | Disable paged KV-cache (paged is **on** by default for `serve`) |
| `--no-prompt-cache` | | false | Disable KV-cache reuse across requests |
| `--prompt-cache-size` | | `4` | Max cached sessions |
| `--no-warmup` | | false | Disable startup warm-up passes |
| `--warmup-iterations` | | `3` | Warm-up iteration count |
> **Short-flag gotcha:** `-p` is **prompt** under `run` but **port** under `serve`. `-s` is **seed** under `run` but **system** under `chat`. When in doubt, use the long form.
## Development
### Debug build
Building in `Debug` configuration (`-c Debug`) enables a `debug` command group with diagnostic tools for inspecting GGUF files and model internals. These commands are excluded from Release builds via `#if DEBUG`.
```bash
# Build in Debug mode
dotnet build src/DotLLM.Cli -c Debug
# Inspect GGUF file structure
dotnet run --project src/DotLLM.Cli -c Debug -- debug gguf-header model.gguf
dotnet run --project src/DotLLM.Cli -c Debug -- debug gguf-metadata model.gguf
dotnet run --project src/DotLLM.Cli -c Debug -- debug gguf-tensors model.gguf
dotnet run --project src/DotLLM.Cli -c Debug -- debug gguf-config model.gguf
# Tokenizer round-trip verification
dotnet run --project src/DotLLM.Cli -c Debug -- debug tokenize model.gguf --text "Hello world"
# Single forward pass with top-10 logit diagnostics
dotnet run --project src/DotLLM.Cli -c Debug -- debug forward-pass model.gguf --prompt "Hello"
# Inspect embedding vector for a token ID
dotnet run --project src/DotLLM.Cli -c Debug -- debug embed-lookup model.gguf --token-id 1
```
| Command | Description |
|---------|-------------|
| `debug gguf-header` | GGUF header structure (magic, version, tensor/metadata counts) |
| `debug gguf-metadata` | All metadata key-value pairs |
| `debug gguf-tensors` | Tensor descriptors (name, shape, quantization type, offset) |
| `debug gguf-config` | Extracted `ModelConfig` (architecture, layers, dims, RoPE params) |
| `debug tokenize` | Encode text → token IDs → decode, verify round-trip fidelity |
| `debug forward-pass` | Single forward pass, top-10 predicted tokens with softmax probabilities |
| `debug embed-lookup` | Raw embedding vector for a given token ID |
> Debug builds are significantly slower than Release (~2-10x) because JIT optimizations, inlining, and SIMD vectorization are reduced. Always use `-c Release` for performance measurements.
### Tests
**Unit and integration tests:**
```bash
dotnet test
```
> Integration tests automatically download several GGUF models (~4.5 GB total) from HuggingFace to `~/.dotllm/test-cache/` on first run. The first `dotnet test` will take a while; subsequent runs use the cache. To run only unit tests (no downloads): `dotnet test tests/DotLLM.Tests.Unit`.
Set `DOTLLM_TEST_CACHE_DIR` to relocate that cache — useful for pointing CI at a mounted cache volume so models aren't re-downloaded every job, or for moving it off a small system drive:
```bash
# Linux/macOS
export DOTLLM_TEST_CACHE_DIR=/mnt/ci-cache/dotllm-models
```
```powershell
# Windows
$env:DOTLLM_TEST_CACHE_DIR = "D:\dotllm-test-cache"
```
The cache uses the same `{owner}/{repo}/{file}.gguf` layout as `~/.dotllm/models/`, so you can point it at your CLI model directory to reuse models you already pulled:
```powershell
$env:DOTLLM_TEST_CACHE_DIR = "$HOME\.dotllm\models"
```
> Doing so means test models are downloaded into your `dotllm model pull` inventory and will appear in `dotllm model list`. That is intentional but opt-in — leave the variable unset to keep auto-downloaded test models separate from models you pulled explicitly. The value must be an absolute path (or a relative one, resolved against the working directory); `~` is not expanded.
> **GPU tests** (tagged `Category=GPU`) require an NVIDIA GPU and run full model inference — they can take 20-30 minutes. They are skipped automatically on machines without CUDA. To exclude them explicitly: `dotnet test tests/DotLLM.Tests.Unit/ --filter "Category!=GPU"`
**Model correctness smoke tests** (`scripts/test_models.py`) run dotLLM CLI with greedy decoding across architectures (Llama, Mistral, Phi, Qwen) and verify expected output:
```bash
# Build CLI first
dotnet build src/DotLLM.Cli -c Release
# List available test cases and which models are cached
python scripts/test_models.py --list
# Run tests for all cached models
python scripts/test_models.py
# Download missing models and run all tests
python scripts/test_models.py --download
# Run only specific architectures
python scripts/test_models.py --filter phi,qwen
```
Models are downloaded from HuggingFace to `~/.dotllm/models/` on first use and cached for subsequent runs.
Sample output:
```
Test Arch Result Time Details
=====================================================================================================
SmolLM-135M Llama PASS 2.1s Paris (163.3 tok/s)
Llama-3.2-1B-Instruct-Q4 Llama PASS 5.7s Paris (31.0 tok/s)
Qwen2.5-0.5B-Instruct Qwen PASS 3.2s Paris (78.5 tok/s)
Phi-3-mini-4k-instruct Phi PASS 12.4s Paris (14.2 tok/s)
=====================================================================================================
4/4 passed, 0 failed, 0 skipped
```
### Benchmarks
Three scripts in `scripts/` provide benchmarking at different levels:
**`bench_compare.py`** -- Single-point benchmark. Runs dotLLM (via [BenchmarkDotNet](https://benchmarkdotnet.org/)) and optionally [llama.cpp](https://github.com/ggerganov/llama.cpp) on one or more models, reports best-of-N throughput with CV (coefficient of variation):
```bash
# Benchmark dotLLM on SmolLM-135M (auto-downloads from HuggingFace)
python scripts/bench_compare.py --model QuantFactory/SmolLM-135M-GGUF --quant Q8_0
# Benchmark multiple models and quantizations
python scripts/bench_compare.py \
--model QuantFactory/SmolLM-135M-GGUF,bartowski/Llama-3.2-1B-Instruct-GGUF \
--quant Q4_K_M,Q8_0
# Compare dotLLM vs llama.cpp side-by-side
python scripts/bench_compare.py --model QuantFactory/SmolLM-135M-GGUF --dotllm --llamacpp
# Export results to JSON for later comparison
python scripts/bench_compare.py --model QuantFactory/SmolLM-135M-GGUF \
--export-json benchmarks/results/baseline.json --label baseline
```
Sample output:
```
=== dotLLM Benchmark Results ===
Model Prefill tok/s Decode tok/s Decode ms/tok Total tok/s CV
SmolLM-135M.Q8_0 229.2 182.7 5.47 175.3 14.7%
SmolLM-135M.Q4_K_M 165.0 230.1 4.35 198.2 20.5%
All values are best-of-N (max tok/s, min ms). CV is the coefficient of variation
across N iterations -- lower means more stable measurements.
```
**`bench_trend.py`** -- Interactive comparison of exported JSON results. Displays color-coded delta tables with noise-aware highlighting:
```bash
# Interactive mode: select runs and models to compare
python scripts/bench_trend.py
# Compare two specific result files
python scripts/bench_trend.py benchmarks/results/baseline.json benchmarks/results/optimized.json
# Show all results as a trend table
python scripts/bench_trend.py --all
```
Sample output (trend across three labeled runs):
```
Benchmark Trend
Label Date Model Prefill tok/s Decode tok/s CV
baseline 2026-03-11 SmolLM-135M.Q4_K_M 127.6 109.5 -
step29 2026-03-13 SmolLM-135M.Q4_K_M 142.2 127.0 -
step30 2026-03-13 SmolLM-135M.Q4_K_M 146.0 98.6 -
```
**`bench_history.py`** -- Benchmark across git commits. Creates worktrees for each commit, runs bench_compare in each, and displays trend tables with per-commit deltas:
```bash
# Benchmark last 5 commits on main
python scripts/bench_history.py myrun --last 5
# Benchmark from a specific commit to HEAD
python scripts/bench_history.py myrun --from f3d3bf8
# Show results from a previous run (no benchmarking)
python scripts/bench_history.py myrun --show
# Interactively select which commits to benchmark
python scripts/bench_history.py myrun --last 10 --select
```
Sample output:
```
Benchmark History -- Llama-3.2-3B-Instruct-Q8_0
Label Date Prefill tok/s %chg pf Decode tok/s %chg dc CV
test_run_0 (f3d3bf8) 2026-03-11 21.2 7.4 3.8%
test_run_1 (cdb5234) 2026-03-12 24.9 +17.5% 8.0 +8.1% 2.1%
test_run_2 (a062743) 2026-03-13 24.5 ~-1.6% 7.8 ~-2.5% 4.5%
test_run_3 (6c06fbf) 2026-03-14 24.6 ~+0.4% 7.8 ~+0.0% 3.2%
test_run_4 (d1978d2) 2026-03-15 25.4 +3.3% 7.0 -10.3% 5.1%
test_run_5 (572179d) 2026-03-16 25.5 ~+0.4% 7.8 +11.4% 4.2%
```
> `%chg` columns show commit-to-commit deltas. `~` prefix means the change is within noise (CV threshold). CV requires multiple [BenchmarkDotNet](https://benchmarkdotnet.org/) iterations (controlled by `--runs` in bench_compare).
> **Why best-of-N instead of median?** On a non-isolated machine, run-to-run noise is typically 6--30%. The median includes runs degraded by OS scheduling jitter, thermal throttling, and background I/O. Best-of-N (maximum throughput) represents what the hardware *can* achieve and is more stable across sessions. CV is reported alongside so you can judge measurement quality -- if CV is high, the environment was noisy and even the best-of-N value should be taken with a grain of salt.
### [llama.cpp](https://github.com/ggerganov/llama.cpp) setup
To run comparison benchmarks against [llama.cpp](https://github.com/ggerganov/llama.cpp):
1. **Get llama.cpp** -- either download a [prebuilt release](https://github.com/ggerganov/llama.cpp/releases) or build from source:
```bash
git clone https://github.com/ggerganov/llama.cpp.git
cd llama.cpp && cmake -B build -DCMAKE_BUILD_TYPE=Release && cmake --build build --config Release
```
2. **Point bench_compare to the binary** -- either:
- Set `LLAMACPP_BIN` environment variable to the path of `llama-completion` (older builds name it `llama-cli`)
- Or pass `--llamacpp-bin /path/to/llama-completion` on each invocation
Without either, bench_compare looks for `llama-completion` on `PATH`.
3. **Run comparison -- match the thread count:**
```bash
python scripts/bench_compare.py --model QuantFactory/SmolLM-135M-GGUF --dotllm --llamacpp --threads 16
```
> **Always pass `--threads` when comparing engines.** Their defaults are different: dotLLM auto-selects *all logical cores*, llama.cpp auto-selects *physical cores*. On a 32-logical/16-physical machine that is 32 vs 16, which makes the comparison meaningless. bench_compare warns when you omit it.
### Comparing .NET runtimes
`--runtimes` runs dotLLM on several target frameworks in one comparison, each as a separate BenchmarkDotNet job with its own result row:
```bash
python scripts/bench_compare.py --model QuantFactory/SmolLM-135M-GGUF \
--dotllm --llamacpp --threads 16 --runtimes net10.0,net11.0
```
```
Decode Throughput (tok/s) * = fastest (highest)
Model Quant llama.cpp dotLLM (net10.0) dotLLM (net11.0)
SmolLM-135M Q8_0 304.6 360.3 417.0*
```
Each requested framework needs an SDK able to target it -- benchmarking `net11.0` requires the .NET 11 SDK installed, otherwise the job fails with `NETSDK1045`. `global.json` uses `rollForward: latestMajor` with `allowPrerelease`, so machines with only the .NET 10 SDK keep resolving to it and the default (host runtime only) is unaffected.
> Do **not** set `DOTNET_ROLL_FORWARD` to benchmark a newer runtime. BenchmarkDotNet generates its toolchain project to match the *host* runtime, so rolling the host forward makes it emit a project the pinned SDK cannot build. `--runtimes` is the supported mechanism.
> [llama.cpp](https://github.com/ggerganov/llama.cpp) is optional. All dotLLM benchmarks work without it. The `--llamacpp` flag simply adds a side-by-side comparison column.
## NuGet Packages
dotLLM ships as a set of NuGet packages so you can reference only what you need from your own .NET app:
| Package | Description |
|---------|-------------|
| [`DotLLM.Core`](https://www.nuget.org/packages/DotLLM.Core) | Core abstractions — tensor types, backend interfaces, model config, sampling, attention strategies, diagnostics hooks |
| [`DotLLM.Cpu`](https://www.nuget.org/packages/DotLLM.Cpu) | CPU backend — SIMD-optimized quantized matmul, RMSNorm, RoPE, softmax, attention |
| [`DotLLM.Cuda`](https://www.nuget.org/packages/DotLLM.Cuda) | CUDA GPU backend — PTX kernels via CUDA Driver API, cuBLAS prefill, CPU/GPU hybrid offload |
| [`DotLLM.Models`](https://www.nuget.org/packages/DotLLM.Models) | Memory-mapped GGUF/SafeTensors loaders, parameterized `TransformerBlock` (Llama/Mistral/Phi/Qwen/DeepSeek) |
| [`DotLLM.Tokenizers`](https://www.nuget.org/packages/DotLLM.Tokenizers) | BPE, SentencePiece, HuggingFace tokenizer.json, Jinja2-subset chat templates |
| [`DotLLM.Engine`](https://www.nuget.org/packages/DotLLM.Engine) | Inference engine — KV-cache, scheduler, samplers, constrained decoding, speculative decoding |
| [`DotLLM.Server`](https://www.nuget.org/packages/DotLLM.Server) | OpenAI-compatible HTTP server, tool calling, built-in chat UI |
| [`DotLLM.HuggingFace`](https://www.nuget.org/packages/DotLLM.HuggingFace) | HuggingFace Hub search and GGUF download/caching |
| [`DotLLM.Diagnostics`](https://www.nuget.org/packages/DotLLM.Diagnostics) | Interpretability hooks — activation capture, logit lens, logprobs |
| [`DotLLM.Telemetry`](https://www.nuget.org/packages/DotLLM.Telemetry) | Placeholder package; `System.Diagnostics.Metrics` counters and `Activity`-based tracing are planned (Phase 7 / Phase 9) |
| [`DotLLM.Cli`](https://www.nuget.org/packages/DotLLM.Cli) | `dotnet tool` — the `dotllm` command (run / chat / serve / model management) |
Install the engine plus CPU backend for a minimal setup:
```bash
dotnet add package DotLLM.Engine
dotnet add package DotLLM.Cpu
dotnet add package DotLLM.Models
dotnet add package DotLLM.Tokenizers
```
Or install the CLI as a global tool:
```bash
dotnet tool install -g DotLLM.Cli
```
> All packages track the same version and are published together on each release.
### Host the OpenAI API in your ASP.NET app
`DotLLM.Server` is a library — reference it from your own ASP.NET Core host to expose dotLLM's OpenAI-compatible routes. Two patterns are supported.
**Mode 1 — run a dedicated dotLLM WebApplication inside your process.** Simplest; you hand off model loading and routing to dotLLM.
```csharp
using DotLLM.Server;
var options = new ServerOptions
{
Model = "llama-3.2-3b.Q4_K_M.gguf",
Device = "gpu",
GpuLayers = 32,
Port = 8080,
PromptCacheEnabled = true,
UsePaged = true,
};
using var state = ServerStartup.LoadModel(options.Model, options);
var app = ServerStartup.BuildApp(state, args: [], serveUi: true);
await app.RunAsync($"http://localhost:{options.Port}");
```
**Mode 2 — attach dotLLM routes to your own `WebApplication`.** Use this when you want dotLLM's endpoints alongside your own routes, middleware, and services.
```csharp
using DotLLM.Server;
var builder = WebApplication.CreateBuilder(args);
// Load the model up front so the state can be registered in DI
// before builder.Build() — dotLLM endpoints resolve ServerState from DI.
var modelPath = "llama-3.2-3b.Q4_K_M.gguf";
var state = ServerStartup.LoadModel(modelPath, new ServerOptions
{
Model = modelPath,
Device = "cpu",
PromptCacheEnabled = true,
ModelId = "llama-3.2-3b",
});
// Your own services...
builder.Services.AddAuthentication();
// Register dotLLM's state + JSON context in the same IServiceCollection:
builder.Services.AddDotLLM(state);
var app = builder.Build();
// Your own middleware and routes...
app.UseAuthentication();
app.MapGet("/hello", () => "hi");
// Mount dotLLM's OpenAI-compatible endpoints:
app.MapDotLLMEndpoints(serveUi: false);
app.Run();
```
Both modes transparently reuse the embedded chat UI assets if `serveUi: true`. The `ServerState` is `IDisposable` — dispose it on shutdown to release the model and KV-cache.
## News
- **2026-04** — **First public release (v0.1.0-preview.1)** — dotLLM goes public. [NuGet packages](#nuget-packages) for all 10 libraries + `DotLLM.Cli` as a global `dotnet tool`. Self-contained single-file downloads for Windows / Linux / macOS (Apple Silicon) and experimental Native AOT builds for Linux / Windows attached to every [GitHub Release](https://github.com/kkokosa/dotLLM/releases). Companion website at [dotllm.dev](https://dotllm.dev/) ([#119](https://github.com/kkokosa/dotLLM/issues/119))
- **2026-04** — **Wave 7**: CPU performance cleanup pass — `TopKSampler` replaces full `Array.Sort` with a hand-rolled size-K min-heap (`O(N log K)`, stack-resident scratch); `JsonSchemaConstraint` adds first-char bucketing to skip the ~160 MB of struct clones per mask build when the tracker rejects most leading characters, plus LRU eviction instead of the previous full-flush cache overflow; `Dequantize.Q5_0` gains an AVX2 path matching Q8_0's throughput (reuses `MatMulQ5_0.ExtractQ5HighBits` / `vpshufb` bit-extraction); `BpeTokenizer` pre-splits special tokens via the existing `Trie.TryMatchLongest` instead of the O(n × m) linear scan; `ComputeThreadPool` now pins the caller (inference) thread to the first candidate P-core on first `Dispatch`, eliminating the hybrid-CPU stall where pinned P-core workers idled at the barrier waiting for an E-core caller. New BenchmarkDotNet suites for TopK sampling, schema mask build, and special-token encode ([#109](https://github.com/kkokosa/dotLLM/issues/109))
- **2026-04** — **Phase 7 begins**: Logprobs — OpenAI-compatible `logprobs: true` + `top_logprobs: N` (0-20) on `/v1/chat/completions` and `/v1/completions`. Per-token log-softmax captured before sampling, returned in both streaming SSE chunks and non-streaming responses. Chat UI gains opt-in logprobs visualization: color-coded token confidence (green/lime/yellow/orange/red), hover tooltips with top-K alternatives and probabilities, diagnostic cues for low confidence, ambiguity, and sampling effect. `DotLLM.Sample.Logprobs` console sample with ANSI-colored output ([#101](https://github.com/kkokosa/dotLLM/issues/101))
- **2026-04** — **Phase 6 complete**: Speculative decoding — draft-verify-accept loop with modified rejection sampling. A small draft model proposes K candidate tokens; the target model verifies in one batched forward pass. Greedy fast-path for temperature=0. `IKvCache.Rollback()` for KV-cache truncation on rejection. `IDecodingConstraint.Clone()` for constraint rollback. Serve UI gains draft model selector and K slider. `--speculative-model` and `--speculative-k` CLI options. Speculative acceptance rate in response timings ([#98](https://github.com/kkokosa/dotLLM/issues/98))
- **2026-04** — Paged KV-cache — block-based KV-cache memory management (the allocation half of PagedAttention): shared block pool, block tables, ref counting, copy-on-write. Foundation for advanced prefix sharing (step 37, hard requirement) and speculative decoding (step 43, cheap rollback/fork). Attention kernels still operate on contiguous buffers via staging-buffer gather — true paged attention kernels are a future step. `--paged` (opt-in for CLI), on by default for `serve`. `--no-ui` flag for API-only hosting. See [docs/KV_CACHE.md](docs/KV_CACHE.md) ([#96](https://github.com/kkokosa/dotLLM/issues/96))
- **2026-04** — Native AOT (experimental) — opt-in `dotnet publish -p:PublishAot=true` produces a single-file `dotllm` binary with ~50ms startup (vs ~500ms JIT). Source-generated JSON serialization across all projects, `CreateSlimBuilder` for ASP.NET, `TrimmerRoots.xml` for Spectre.Console.Cli type preservation. JIT remains default for best throughput (Dynamic PGO). See [docs/AOT.md](docs/AOT.md) ([#94](https://github.com/kkokosa/dotLLM/issues/94))
- **2026-04** — **Phase 6 begins**: Warm-up — configurable dummy inference passes at server startup trigger .NET Tier-1 JIT promotion (Dynamic PGO) and exercise CUDA/cuBLAS pipelines. `/ready` probe gates on warm-up completion. `--no-warmup` to disable, `--warmup-iterations N` to configure ([#92](https://github.com/kkokosa/dotLLM/issues/92))
- **2026-04** — **Phase 5 complete**: simple prompt caching completes the constrained decoding & API phase — all 7 steps done (JSON mode, JSON Schema, regex/CFG, tool calling, API server, chat UI, prompt caching)
- **2026-04** — Simple prompt caching — `PrefixCache` keeps live KV-cache instances across generation calls. On each turn, element-wise prefix match finds cached tokens and skips redundant prefill, processing only new suffix tokens. LRU eviction with configurable max sessions. Enabled by default in `chat` and `serve` commands (`--no-prompt-cache` to disable). Cached token count reported in CLI stats, API `timings.cached_tokens`, and Chat UI stats bar. Near-100% cache hit rate in multi-turn chat, dramatically reducing TTFT on subsequent turns ([#90](https://github.com/kkokosa/dotLLM/issues/90))
- **2026-04** — Built-in web chat UI — `dotllm serve model.gguf` starts the API server and opens a browser to a bundled single-page chat UI (vanilla JS + TailwindCSS, embedded as resources in the DLL). Per-message inference stats (prefill/decode tok/s, TTFT), live sampling parameter control, model hot-swap from the UI, system prompt, verbose mode. New endpoints: `/props`, `/v1/config`, `/v1/models/available`, `/v1/models/load`. Streaming SSE now includes `usage` + `timings` in the final chunk ([#86](https://github.com/kkokosa/dotLLM/issues/86))
- **2026-04** — ASP.NET OpenAI-compatible API server — `DotLLM.Server` with `/v1/chat/completions` (streaming SSE + non-streaming), `/v1/completions`, `/v1/models`, `/v1/tokenize`, `/v1/detokenize`, health/ready probes. Chat template formatting, tool calling with `IToolCallParser` detection, `response_format` constrained decoding. Model loading at startup via `--model`/`--device` CLI args. Sequential request processing via semaphore ([#84](https://github.com/kkokosa/dotLLM/issues/84))
- **2026-04** — Tool calling — `IToolCallParser` implementations for Llama 3.1+, Hermes/Qwen, Mistral, and generic fallback. Auto-detection factory selects parser from model architecture and chat template content. `ToolCallSchemaBuilder` generates JSON Schema from tool definitions for constrained decoding (`tool_choice=required`). `ToolCallDetector` for post-generation detection, `StreamingToolCallAccumulator` for streaming. `--tools` and `--tool-choice` CLI options with multi-turn tool use in chat REPL. Parallel tool calls supported ([#82](https://github.com/kkokosa/dotLLM/issues/82))
- **2026-04** — Regex + CFG constrained decoding — `RegexConstraint` compiles patterns to minimized DFA (Thompson NFA → subset construction → Hopcroft minimization) with equivalence-class compression. `GrammarConstraint` parses GBNF grammars into PDA with InlineArray-based call stack. Both use zero-alloc struct simulators and dictionary-cached token masks. `--response-format regex --pattern ` and `--response-format grammar --grammar ` CLI support ([#80](https://github.com/kkokosa/dotLLM/issues/80))
- **2026-03** — JSON Schema constrained decoding — `JsonSchemaConstraint` layers schema tracking on `JsonCharParser` to enforce type constraints, required properties, enum values, nested structures. Schema compiled into flat node array with property-name tries. Zero-alloc `Clone()` via struct-copy. `--response-format json_schema --schema ` CLI support ([#78](https://github.com/kkokosa/dotLLM/issues/78))
- **2026-03** — **Phase 5 begins**: JSON mode constrained decoding — `JsonConstraint` FSM guarantees syntactically valid JSON output via per-token vocabulary masking. Stack-based PDA (RFC 8259), AVX2-vectorized logit masking, state-keyed mask cache. `--response-format json_object` CLI flag ([#76](https://github.com/kkokosa/dotLLM/issues/76))
- **2026-03** — KV-cache quantization: Q8_0 and Q4_0 KV-cache compression on CPU and GPU (3.7–7.1× memory reduction). Separate `--cache-type-k`/`--cache-type-v` options, mixed-precision window `--cache-window N` keeps recent tokens in full precision. Dual-region storage with quantize-on-evict, per-tile dequantization in tiled attention ([#74](https://github.com/kkokosa/dotLLM/issues/74))
- **2026-03** — CPU/GPU hybrid layer offloading: `--gpu-layers N` to run first N layers on GPU, remainder on CPU. Automatic FP16→FP32 hidden state transfer at boundary. Split KV-cache (GPU FP16 + CPU FP32). Partial VRAM usage proportional to offloaded layers ([#72](https://github.com/kkokosa/dotLLM/issues/72))
- **2026-03** — CUDA GPU backend: PTX kernels via CUDA Driver API P/Invoke (no native shared library), cuBLAS HGEMM for prefill, custom quantized GEMV for decode (Q8_0, Q4_K, Q6_K), FP16 activation pipeline, on-the-fly weight dequantization, GPU KV-cache, `--device gpu` CLI flag, `--device both` benchmarking ([#70](https://github.com/kkokosa/dotLLM/issues/70))
- **2026-03** — NUMA-aware threading: adaptive spin-wait dispatch (generation counter with event fallback), NUMA topology detection (Windows/Linux), P-core/E-core awareness, CPU affinity pinning, auto-reduced decode thread count ([#57](https://github.com/kkokosa/dotLLM/issues/57))
- **2026-03** — Operator fusion: fused RMSNorm+quantize (decode-only, eliminates normOut intermediate buffer) and tiled SwiGLU (1KB L1-resident sigmoid buffer) reduce DRAM roundtrips on the decode hot path ([#56](https://github.com/kkokosa/dotLLM/issues/56))
- **2026-03** — Fast approximate exp/softmax: Schraudolph IEEE-754 bit-manipulation trick replaces polynomial exp (~3 SIMD ops vs ~12) in attention softmax. AVX2/AVX-512 fused shift+exp+sum pass eliminates 3 separate TensorPrimitives calls. Sampling softmax keeps full precision ([#55](https://github.com/kkokosa/dotLLM/issues/55))
- **2026-03** — Tiled attention with online softmax: O(N) memory flash-attention-style algorithm replaces O(N²) score matrix materialization, eliminates 64 MB/head allocations at ctx 4096, uses ~1 KB stack per head ([#54](https://github.com/kkokosa/dotLLM/issues/54))
- **2026-03** — Row-interleaved weight repacking: R4 layout stores 4 consecutive rows' blocks contiguously at model load time, improving cache/TLB locality for all quantized GEMV kernels ([#52](https://github.com/kkokosa/dotLLM/issues/52))
- **2026-03** — Q8_1 input quantization: precomputed block sums for Q5_0 kernels, 2-block loop unrolling, eliminates ~4 SIMD ops/block from Q5_0 vec_dot hot path ([#51](https://github.com/kkokosa/dotLLM/issues/51))
- **2026-03** — Fused decode dispatch: Q/K/V (3→1) and Gate/Up (2→1) projection fusion saves ~72 dispatches/layer, ~4% decode throughput improvement ([#50](https://github.com/kkokosa/dotLLM/issues/50))
- **2026-03** — **Phase 2 complete**: additional model architectures (Mistral, Phi, Qwen), sliding window attention, fused QKV support, `IModel` interface, `ModelLoader` helper ([#34](https://github.com/kkokosa/dotLLM/issues/34))
- **2026-03** — Streaming token generation: `IAsyncEnumerable` API with UTF-8-safe incremental text, `CancellationToken` support, and per-token finish reason/timings ([#31](https://github.com/kkokosa/dotLLM/issues/31))
- **2026-03** — Chat template engine: Jinja2-subset interpreter (lexer→parser→evaluator), `IChatTemplate` implementation, `GgufChatTemplateFactory`, `dotllm chat` REPL command ([#30](https://github.com/kkokosa/dotLLM/issues/30))
- **2026-03** — Mixed quantization + Q8_K: Q8_K input quantization (float32 scale, 256-element blocks, precomputed bsums), true 4-row fused K-quant kernels, re-enabled Q4_K×Q8_K/Q5_K×Q8_K/Q6_K×Q8_K fused GEMV/GEMM ([#29](https://github.com/kkokosa/dotLLM/issues/29))
- **2026-03** — Q4_K_M dequantization and vec_dot kernels: Q4_K, Q5_K, Q6_K scalar + AVX2 dequant and fused matmul kernels with full model-level dispatch ([#28](https://github.com/kkokosa/dotLLM/issues/28))
- **2026-03** — BDN inference benchmarks: end-to-end benchmarks with custom tok/s columns, auto model download, llama.cpp comparison script ([#42](https://github.com/kkokosa/dotLLM/issues/42))
- **2026-03** — Engine inference timings: `InferenceTimings` on `InferenceResponse`, `onTokenGenerated` callback, CLI refactored to use `TextGenerator` ([#41](https://github.com/kkokosa/dotLLM/issues/41))
- **2026-03** — Multi-threaded CPU inference: zero-alloc `ComputeThreadPool` with `delegate*` dispatch, parallel GEMV/GEMM and head-parallel attention ([#36](https://github.com/kkokosa/dotLLM/issues/36))
- **2026-03** — SIMD kernel tuning: FMA float accumulation, 4-row batched GEMV, AVX-512 paths, SIMD quantization ([#26](https://github.com/kkokosa/dotLLM/issues/26))
- **2026-03** — Phase 1 complete: sampling pipeline + stop conditions — first coherent multi-token generation ([#24](https://github.com/kkokosa/dotLLM/pull/24))
- **2026-03** — KV-cache: eval drops from 1091 ms/token to 227 ms/token (~4.8× speedup)
- **2026-03** — Llama forward pass: first token generation from embedding to logits
- **2026-02** — BPE Tokenizer with SentencePiece and tiktoken support ([#16](https://github.com/kkokosa/dotLLM/pull/16))
## Roadmap
| Phase | Description | Status |
|-------|-------------|--------|
| **1 — End-to-End Generation** | GGUF loading, dequantization, CPU ops, tokenizer, attention, forward pass, KV-cache, sampling | Done (9/9) |
| **2 — Practical Local Inference** | Engine metrics, benchmarks, Q4_K_M, chat templates, streaming, multi-threading, more architectures | Done (10/10) |
| **3 — CPU Performance** | Decode dispatch, Q8_1 input, weight repacking, outer-product GEMM, tiled attention, fast exp, fusion, NUMA | In Progress (7/8) |
| **4 — GPU Acceleration** | CUDA backend, CPU/GPU hybrid, KV-cache quantization | Done (3/3) |
| **5 — Constrained Decoding & API** | JSON mode, JSON Schema, regex/CFG, tool calling, OpenAI API server, chat UI, prompt caching | Done (7/7) |
| **6 — Improved Serving** | Warm-up, NativeAOT, paged KV-cache, speculative decoding | Done (4/4) |
| **7 — Diagnostics & Interpretability** | Logprobs, hook system, logit lens, SAE integration, LoRA adapters | In Progress (1/5) |
| **8 — Model Expansion** | MLA attention, ALiBi, SmolLM3, Gemma 4, Mixture of Experts | Planned (0/5) |
| **9 — Production Serving** | Continuous batching, prefix sharing, advanced scheduling, rate limiting, metrics & tracing | Planned (0/5) |
See [docs/ROADMAP.md](docs/ROADMAP.md) for detailed steps, dependencies, and milestones.
## Documentation
- [Architecture & data flow](docs/ARCHITECTURE.md)
- [GGUF binary format](docs/GGUF_FORMAT.md)
- [Quantization formats](docs/QUANTIZATION.md)
- [Attention mechanisms](docs/ATTENTION.md)
- [Position encoding](docs/POSITION_ENCODING.md)
- [Tokenizers & chat templates](docs/TOKENIZERS.md)
- [Sampling pipeline](docs/SAMPLING.md)
- [Constrained decoding](docs/CONSTRAINED_DECODING.md)
- [Tool calling](docs/TOOL_CALLING.md)
- [KV-cache management](docs/KV_CACHE.md)
- [GPU inference](docs/GPU.md)
- [CUDA backend architecture](docs/CUDA.md)
- [Batch scheduling](docs/SCHEDULING.md)
- [Native AOT deployment](docs/AOT.md)
- [Full roadmap](docs/ROADMAP.md)
## Contributing
Contributions are welcome! dotLLM uses an issue-driven workflow — every change starts with a [GitHub issue](https://github.com/kkokosa/dotLLM/issues) describing the work. Pick an existing issue or open a new one, then submit a PR targeting `main`.
## Contact
Questions, ideas, or feedback? Open a thread in [GitHub Discussions](https://github.com/kkokosa/dotLLM/discussions).
## Author
Built by **[Konrad Kokosa](https://kokosa.dev/)** — .NET MVP, author of *Pro .NET Memory Management* (2nd ed.), and AI/agents engineer at Nethermind. Over 20 years of .NET performance work.
- Website: [dotllm.dev](https://dotllm.dev/)
- Personal: [kokosa.dev](https://kokosa.dev/)
- GitHub: [@kkokosa](https://github.com/kkokosa)
## License
dotLLM is licensed under the [GNU General Public License v3.0](LICENSE).
## Acknowledgments
- [llama.cpp](https://github.com/ggerganov/llama.cpp) — reference for GGUF format, quantization kernels, and CUDA implementations
- [Hugging Face](https://huggingface.co/) — model ecosystem, transformers reference implementations, tokenizer specs
- [.NET team](https://github.com/dotnet/runtime) — `TensorPrimitives`, `System.Runtime.Intrinsics`, `MemoryMappedFile`, and the runtime that makes this possible