Run frontier-scale MoE LLMs on a handful of VRAM-constrained consumer GPUs by streaming experts over PCIe.
C++
5
37 commits
updated Sep 8, 2026
Run frontier-scale MoE LLMs on a handful of VRAM-constrained consumer GPUs by streaming experts over PCIe.
| Family | Architecture | Notes |
|---|---|---|
| RTX 50xx | NVIDIA SM120 | 5090 / 5080 tested; kernels build as 120f |
| Model | Size |
|---|---|
| GLM-5.3-Flash | 320 B (18 B active) |
| GLM-5.2 | 744 B (40 B active) |
| DeepSeek-V4-Flash | 284 B (13 B active) |
| Scheme | Bits |
|---|---|
| 1. TurboQuant | 4.03–5.36 |
| 2. FP8 + BF16 RoPE (SnapMLA-style), almost lossless | 8.06–8.94 |
| Format | Bits |
|---|---|
| FP8 | 8 |
| NVFP4 | 4 |
| GGUF — e.g. UD-Q4_K_XL | 2–8 (mixed) |
LayerStoRm fits large MoE LLMs, with large context, into much smaller VRAM — efficiently. MoE models are almost entirely routed experts, but each token activates only a few — and the router tells you which. LayerStoRm keeps the full expert set in pinned host RAM and streams the activated experts to the GPUs every token, overlapped with compute.
| Config | GPUs | Host RAM | Interconnect | Total VRAM |
|---|---|---|---|---|
| #1 | 2× RTX 5090 + 2× RTX 5080 | 512 GB (NUMA-placed) + 64 GB HBM | PCIe 5.0 | 96 GB |
All numbers measured on Config #1.
| Model | Config | Quant | Weights on disk | Draft | Ctx length | KV cache quant | Prefill (tok/s) | Decode (tok/s) | TTFT |
|---|---|---|---|---|---|---|---|---|---|
| GLM-5.3-Flash | #1 | UD-Q4_K_XL | 186.0 GiB | (WIP) | 1M | FP8 + BF16 RoPE | 159 @27k | 27.0 @0k 24.5 @8k | — |
| GLM-5.2 | #1 | UD-Q4_K_XL | 435.2 GiB | dspark γ15 (nvfp4, sharded) | 400k | TQ 4-bit + BF16 RoPE | 28 @8k | 6.8 @0.4k 5.3 @8k | — |
Prerequisites (Ubuntu):
sudo apt install build-essential cmake git python3 libnuma-dev liburing-dev libnccl2 libnccl-dev
CUDA (suggested 13+; skip if nvcc --version is already good) — from NVIDIA's repos or installer if apt doesn't have it.
Clone and set up (no sudo from here on; setup.sh is checked in, installs nothing system-wide):
git clone --recursive https://github.com/kkontosis/LayerStoRm.git
cd LayerStoRm
./scripts/setup.sh
Build:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)
CMake finds nvcc, the .venv interpreter and pybind11 on its own, and builds
the Cython fast bridge as part of the normal build. Override any of them with
-DCMAKE_CUDA_COMPILER=…, -DPYTHON_EXECUTABLE=…, -Dpybind11_DIR=… if you
need to.
Get a model (GGUF weights + the model's HF tokenizer/metadata files, downloaded next to the weights — the engine resolves the tokenizer, chat template and generation config from the weights directory first; repo test-data/ is only a loudly-logged fallback):
.toolchain/bin/uv pip install huggingface_hub
.venv/bin/hf download unsloth/GLM-5.3-Flash-GGUF --include "UD-Q4_K_XL/*" --local-dir models/GLM-5.3-Flash-GGUF
.venv/bin/hf download zai-org/GLM-5.3-Flash --exclude "*.safetensors" --local-dir models/GLM-5.3-Flash-GGUF/UD-Q4_K_XL
CUDA_DEVICE_ORDER=PCI_BUS_ID \
.venv/bin/python python/cli/serve.py --autoconfig \
--model models/GLM-5.3-Flash-GGUF/UD-Q4_K_XL/GLM-5.3-Flash-UD-Q4_K_XL-00001-of-00006.gguf \
--max-sequence-length 1048576 --max-concurrent 2 \
--model-name glm-5.3-flash --host 127.0.0.1 --port 8000
curl -s http://127.0.0.1:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model": "glm-5.3-flash", "max_tokens": 128,
"messages": [{"role": "user", "content": "What is the capital of France?"}]}'
Notes, kept short:
1048576 engages KV tiering, which costs ~10–16%; a smaller ask derives a faster untiered config (the hand-tuned 200k champion reaches 27.4 tok/s decode). Untested context lengths are derived the same way but have not been measured.CUDA_DEVICE_ORDER=PCI_BUS_ID keeps CUDA numbering matched to the PCI order the placement assumes (mixed 5090/5080 boxes can otherwise swap GPU roles).LAYERSTORM_DETERMINISTIC_EP_COMBINE=1 for run-to-run reproducible greedy output./v1/completions, streaming SSE, logprobs, tool calls, reasoning content, and guided JSON output all work.docs/SERVING.md — full serving procedure: autoconfig in detail, the calibrate/train/place pipeline, hand-tuning behind the champion recipesdocs/AUTOCONFIG_MODEL.md — config derivation: levers, pins, constraint registry, refusal semanticsdocs/BUILDING.md — full build reference and troubleshootingdocs/INTERNALS.md — subsystem tour and the I8 placement modeldocs/DESIGN.md / docs/I8_PLACEMENT_MODEL.md — designs at reimplementation depthHighly experimental — early research-grade, under rapid development. Interfaces, configs, and on-disk formats change without notice; correctness outside the gated model/hardware combinations is not guaranteed. Not for production.
MIT — see LICENSE.md. Third-party notices in THIRD_PARTY_NOTICES.md: SM120 MLA attention kernels derive from FlashMLA (MIT) and CUTLASS (BSD-3-Clause); CPU/GGUF quantized-GEMM and MXFP4/GGUF decode paths derive from the llama.cpp / ik_llama.cpp lineage (MIT); parts of the serving layer follow vLLM and SGLang (Apache-2.0).
Issues and PRs welcome — measurements from other SM120 boxes especially. Performance claims need a number and its regime; correctness changes need the golden-token gates under tests/ green. Start with docs/DESIGN.md and DEVELOPMENT.md.
The design is drawn from published work. These are influences — the
implementations here are independent unless
THIRD_PARTY_NOTICES.md records that code was
adapted.
Each of these shaped a subsystem that ships:
THIRD_PARTY_NOTICES.md)Read while designing the above; not implemented here.
Attention, KV cache and long context
MoE offloading, expert caching and prefetching
Speculative decoding, early exit and layer skipping
DSpark and Training-Free Loosely Speculative Decoding are listed without links because the copies consulted here carry no canonical URL.
LayerStoRm stands on the shoulders of the open inference ecosystem — for
reference implementations, design ideas, and (where noted in
THIRD_PARTY_NOTICES.md) adapted code:
And thank you to the authors of the work referenced above. Nearly every subsystem here started as someone else's published idea; the measurements in this project's ledgers exist because that work was shared openly. Errors in adapting it are mine.
37 commits
C++
63.8%
Python
27.4%
Cuda
7.5%
Run frontier-scale MoE LLMs on a handful of VRAM-constrained consumer GPUs by streaming experts over PCIe.
C++
5
37 commits
updated Sep 8, 2026
Run frontier-scale MoE LLMs on a handful of VRAM-constrained consumer GPUs by streaming experts over PCIe.
| Family | Architecture | Notes |
|---|---|---|
| RTX 50xx | NVIDIA SM120 | 5090 / 5080 tested; kernels build as 120f |
| Model | Size |
|---|---|
| GLM-5.3-Flash | 320 B (18 B active) |
| GLM-5.2 | 744 B (40 B active) |
| DeepSeek-V4-Flash | 284 B (13 B active) |
| Scheme | Bits |
|---|---|
| 1. TurboQuant | 4.03–5.36 |
| 2. FP8 + BF16 RoPE (SnapMLA-style), almost lossless | 8.06–8.94 |
| Format | Bits |
|---|---|
| FP8 | 8 |
| NVFP4 | 4 |
| GGUF — e.g. UD-Q4_K_XL | 2–8 (mixed) |
LayerStoRm fits large MoE LLMs, with large context, into much smaller VRAM — efficiently. MoE models are almost entirely routed experts, but each token activates only a few — and the router tells you which. LayerStoRm keeps the full expert set in pinned host RAM and streams the activated experts to the GPUs every token, overlapped with compute.
| Config | GPUs | Host RAM | Interconnect | Total VRAM |
|---|---|---|---|---|
| #1 | 2× RTX 5090 + 2× RTX 5080 | 512 GB (NUMA-placed) + 64 GB HBM | PCIe 5.0 | 96 GB |
All numbers measured on Config #1.
| Model | Config | Quant | Weights on disk | Draft | Ctx length | KV cache quant | Prefill (tok/s) | Decode (tok/s) | TTFT |
|---|---|---|---|---|---|---|---|---|---|
| GLM-5.3-Flash | #1 | UD-Q4_K_XL | 186.0 GiB | (WIP) | 1M | FP8 + BF16 RoPE | 159 @27k | 27.0 @0k 24.5 @8k | — |
| GLM-5.2 | #1 | UD-Q4_K_XL | 435.2 GiB | dspark γ15 (nvfp4, sharded) | 400k | TQ 4-bit + BF16 RoPE | 28 @8k | 6.8 @0.4k 5.3 @8k | — |
Prerequisites (Ubuntu):
sudo apt install build-essential cmake git python3 libnuma-dev liburing-dev libnccl2 libnccl-dev
CUDA (suggested 13+; skip if nvcc --version is already good) — from NVIDIA's repos or installer if apt doesn't have it.
Clone and set up (no sudo from here on; setup.sh is checked in, installs nothing system-wide):
git clone --recursive https://github.com/kkontosis/LayerStoRm.git
cd LayerStoRm
./scripts/setup.sh
Build:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)
CMake finds nvcc, the .venv interpreter and pybind11 on its own, and builds
the Cython fast bridge as part of the normal build. Override any of them with
-DCMAKE_CUDA_COMPILER=…, -DPYTHON_EXECUTABLE=…, -Dpybind11_DIR=… if you
need to.
Get a model (GGUF weights + the model's HF tokenizer/metadata files, downloaded next to the weights — the engine resolves the tokenizer, chat template and generation config from the weights directory first; repo test-data/ is only a loudly-logged fallback):
.toolchain/bin/uv pip install huggingface_hub
.venv/bin/hf download unsloth/GLM-5.3-Flash-GGUF --include "UD-Q4_K_XL/*" --local-dir models/GLM-5.3-Flash-GGUF
.venv/bin/hf download zai-org/GLM-5.3-Flash --exclude "*.safetensors" --local-dir models/GLM-5.3-Flash-GGUF/UD-Q4_K_XL
CUDA_DEVICE_ORDER=PCI_BUS_ID \
.venv/bin/python python/cli/serve.py --autoconfig \
--model models/GLM-5.3-Flash-GGUF/UD-Q4_K_XL/GLM-5.3-Flash-UD-Q4_K_XL-00001-of-00006.gguf \
--max-sequence-length 1048576 --max-concurrent 2 \
--model-name glm-5.3-flash --host 127.0.0.1 --port 8000
curl -s http://127.0.0.1:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model": "glm-5.3-flash", "max_tokens": 128,
"messages": [{"role": "user", "content": "What is the capital of France?"}]}'
Notes, kept short:
1048576 engages KV tiering, which costs ~10–16%; a smaller ask derives a faster untiered config (the hand-tuned 200k champion reaches 27.4 tok/s decode). Untested context lengths are derived the same way but have not been measured.CUDA_DEVICE_ORDER=PCI_BUS_ID keeps CUDA numbering matched to the PCI order the placement assumes (mixed 5090/5080 boxes can otherwise swap GPU roles).LAYERSTORM_DETERMINISTIC_EP_COMBINE=1 for run-to-run reproducible greedy output./v1/completions, streaming SSE, logprobs, tool calls, reasoning content, and guided JSON output all work.docs/SERVING.md — full serving procedure: autoconfig in detail, the calibrate/train/place pipeline, hand-tuning behind the champion recipesdocs/AUTOCONFIG_MODEL.md — config derivation: levers, pins, constraint registry, refusal semanticsdocs/BUILDING.md — full build reference and troubleshootingdocs/INTERNALS.md — subsystem tour and the I8 placement modeldocs/DESIGN.md / docs/I8_PLACEMENT_MODEL.md — designs at reimplementation depthHighly experimental — early research-grade, under rapid development. Interfaces, configs, and on-disk formats change without notice; correctness outside the gated model/hardware combinations is not guaranteed. Not for production.
MIT — see LICENSE.md. Third-party notices in THIRD_PARTY_NOTICES.md: SM120 MLA attention kernels derive from FlashMLA (MIT) and CUTLASS (BSD-3-Clause); CPU/GGUF quantized-GEMM and MXFP4/GGUF decode paths derive from the llama.cpp / ik_llama.cpp lineage (MIT); parts of the serving layer follow vLLM and SGLang (Apache-2.0).
Issues and PRs welcome — measurements from other SM120 boxes especially. Performance claims need a number and its regime; correctness changes need the golden-token gates under tests/ green. Start with docs/DESIGN.md and DEVELOPMENT.md.
The design is drawn from published work. These are influences — the
implementations here are independent unless
THIRD_PARTY_NOTICES.md records that code was
adapted.
Each of these shaped a subsystem that ships:
THIRD_PARTY_NOTICES.md)Read while designing the above; not implemented here.
Attention, KV cache and long context
MoE offloading, expert caching and prefetching
Speculative decoding, early exit and layer skipping
DSpark and Training-Free Loosely Speculative Decoding are listed without links because the copies consulted here carry no canonical URL.
LayerStoRm stands on the shoulders of the open inference ecosystem — for
reference implementations, design ideas, and (where noted in
THIRD_PARTY_NOTICES.md) adapted code:
And thank you to the authors of the work referenced above. Nearly every subsystem here started as someone else's published idea; the measurements in this project's ledgers exist because that work was shared openly. Errors in adapting it are mine.
37 commits
C++
63.8%
Python
27.4%
Cuda
7.5%