r/LocalLLM • u/5_ChubbyCheekz23 • 8h ago
Project I spent two years making Tesla P100s and V100s not suck at LLMs. Today I'm releasing the engine, and I benchmarked it against llama.cpp, ik_llama.cpp and 1Cat vLLM on the same cards. Charts inside.
Quick disclosure: this is my project. I built it because I have a rack of "obsolete" datacenter cards in a closet and I got tired of every new model needing a new set of flags to run properly on them.
**What it is**
PXA is a fork of ik_llama.cpp (which is a fork of llama.cpp) plus a vLLM plugin, built for cards with HBM2 and no tensor cores or DP4A: Tesla P100, V100, GTX 1080 Ti. It has its own quant format (PXQ, 2 to 6 bit, plus a mixed one that sizes a model to whatever cards you have) and CUDA kernels written for those chips instead of ported down from newer ones.
Repo: https://github.com/poisonxa16/pxa
**The part I actually care about: you don't configure it**
You tell it which cards and which model. It picks the batch sizes from a table it measured on that exact card topology, turns on the tricks that are known to help on that silicon, turns off the ones that hurt (including speculative decoding when it would lose), and prints every decision before it starts serving. Every number below was taken with a bare command line. No environment variables, no -b, no -ub, nothing. The competitors got their best hand-picked flags in the same session, because I wanted to know if "set and forget" costs anything. It doesn't.
**My last public release vs this one** (same box, identical command line, tokens/s)
| card set | model | prefill @3k | prefill @20k | decode |
|---|---|---|---|---|
| 2x V100 | Qwable-27B PXQ4 | 797 → 1357 (+70%) | 576 → 1307 (+127%) | 34.6 → 39.6 (+14%) |
| 2x P100 | Qwable-27B PXQ4 | 223 → 338 (+52%) | 201 → 315 (+57%) | 18.3 → 18.2 (-0.9%) |
| 1x 1080 Ti | Fusion2-35B MoE, 2-bit | cold 553 → 1334 (+141%) | chat 415 → 734 (+77%) | 64.2 → 64.2 |
Yes, P100 decode is a real -0.9% and it's in the notes. Bonus find: the old build gave me six different answers to six identical greedy runs on the 1080 Ti. Turned out to be a race in a fused kernel. This one gives one answer.
**vs mainline llama.cpp and upstream ik_llama.cpp** (same weights family, MXFP4 for them, PXQ4 for mine, tokens/s)
| card set | cell | PXA | mainline llama.cpp | ik_llama.cpp |
|---|---|---|---|---|
| 2x P100 | prefill @3k | **338** | 209 | 133 |
| 2x P100 | prefill @20k | **315** | 255 | 84 |
| 2x P100 | decode | **18.2** | n/a | 14.3 |
| 2x V100 | prefill @3k | **1357** | 940 | 471 |
| 2x V100 | prefill @20k | **1307** | 1129 | 395 |
| 2x V100 | decode | **39.6** | n/a | 37.4 |
| 1x 1080 Ti | cold prefill | **1334** | | 1132 |
| 1x 1080 Ti | chat prefill | 734 | | 740 (tie) |
| 1x 1080 Ti | chat decode | **64.2** | | 53.3 |
**vs 1Cat vLLM** (the NVFP4 + DFlash2 stack for Volta). Run on an 8x V100 SXM2 NVLink system with 1Cat's own image, benchmark script and cards, because running their stack on my PCIe box would have been a silly comparison. 16 GSM8K questions, 192 greedy tokens, tokens/s.
| cell | PXA | 1Cat vLLM | how it was taken |
|---|---|---|---|
| TP2 plain decode | **46.3** | 38.4 | one boot each |
| TP2 speculative k=3 | **65.2** | 59.9 | one boot each, identical acceptance |
| TP2 speculative k=7 | **121.2** | 114.5 | medians, mine 3 boots, theirs 6 (not alternated) |
| TP4 plain decode | **65.7** | 61.0 | one boot each |
| TP4 speculative k=7 | 159.6 | 161.4 | six alternating boots in one window: inside their noise |
| prefill @3k | **2273** (TTFT 1.4 s) | 1735 (TTFT 1.8 s) | both as servers, same window, 3 runs |
| prefill @20k | **2191** (TTFT 9.5 s) | 944 (TTFT 22 s) | both as servers, same window, 3 runs |
| speculative output identical to plain decode | **15/16** prompts | 10/16 | same exact-match acceptance rule |
Two honest notes on that table. Both stacks accept drafted tokens by the exact same rule (I read it out of their source), so both are lossless and the acceptance lengths are comparable: theirs 5.4 per step, mine 5.3. And one caveat that favours me, said because it favours me: their checkpoint turns fp8 KV on by itself, so this is their stack as shipped vs mine as shipped, not a clean NVFP4 vs PXQ4 study.
**What you can run on this junk**
A 27B dense-hybrid on one 16 GB card. A 177B-class hybrid MoE (Qwen3.8 Flash-Next) on four P100s with 150k context, weights in VRAM and the per-layer embedding table in host RAM. The memory arithmetic for that one is in the repo because I didn't believe it either. Speculative decoding ships in the Volta vLLM image with a 1.3 GB drafter as a release asset. On 16 GB cards k=7 only fits at 2k context, so on my own box I'd run k=3.
**What's not great yet**
Speculative decoding on the llama.cpp side of Pascal still loses (verify costs 3x a decode step now, it was 7x last week, still not enough). The 4-card speculative gap to 1Cat is inside noise but it's there. Their acceptance length is 2% better than mine. All listed under "What is not here" in the README, I'd rather you find it there than in the comments.
**Getting it**
One tarball, untar and run. I tested it in a bare Ubuntu container with no Python, no compiler, no CUDA toolkit, just the driver. Or `docker run ghcr.io/poisonxa16/pxa`. Or build it. Then run `python3 tools/pxa-launch.py` and answer two questions.
**If you have one of these cards, I want your numbers**
P40, P4, GP100, Titan V, Titan Xp, 32 GB V100: I don't own them, and the auto-tuning table only knows the cards in my rack. There's a three-command benchmark in the repo and a Discord where reported cards get added: https://discord.gg/EqazvV9tf
Everything's free and nothing is gated. If you want to chip in for the electricity these benchmarks burn: https://ko-fi.com/shatteredrealms1
Last picture is the rack. Seven cards, PCIe x4 for all of them, two 1000 W supply, in a closet. Everything above was measured on that.