Repository navigation
qsa_prompt_attn: Q4_0 KV on tensor cores (mode 4) - long prompts with --kv q4_0 ~19% faster - #452
architectds wants to merge 1 commit into
Conversation
… with --kv q4_0 no longer take the old kernel The tensor-core prompt attention (D-1) refused Q4_0 pools, so --kv q4_0 read every prompt through the FP32 per-query kernel. Mode 4 is mode 1 (int8) with a scale per 32 values: each q4_0 block's 16 code bytes become 32 exact int8 codes (code - 8) at gather, its fp16 scale goes to the per-group scales, which are applied in FP32 as int8's are (q.k per 32-dim group; p.v folds each group's scale into p, two groups per warp). The p.v fold is bounded by the chunk's largest |scale|: q4_0 scales are signed (ggml's d = max / -8). The caller's rotation of q and of the output is unchanged. STRATA_PROMPT_ATTN_Q4=0: the old kernel (A/B). qsa_prompt_attn_parity gets Q4_0 cases with signed scales spread over two decades, as a pool holds them (32K and 1.5K contexts), against FP64 and the old kernel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Next-token comparison at long prompts. This is the method of Setup:
For scale: on 60 shorter prompts (200-1,000 tokens), the engine's own two GPU prompt paths (MMQ against Build: a v0.1.33-based build with this same kernel, plus a serve-mode |
…hile it is loaded (--vision-on-demand; engine --lendable-cache, LEND / RECLAIM)
The engine: --lendable-cache makes the expert cache 64 MiB pieces of one reserved address range (CUDA virtual memory,
looked up through cudaGetDriverEntryPointByVersion, so nothing new is linked). --serve takes two lines between
requests: LEND <MiB> marks the experts in the cache's last MiB as CPU misses, uploads the residency table and unmaps
and frees those pieces (another process can have the VRAM); RECLAIM maps new pieces at the same addresses, refills
every lent slot from the arena (first and last read back; all with STRATA_VERIFY_RECLAIM=1) and marks them resident
again. While the loan is out the adaptive tier leaves the lent experts alone and the prompt path lays its buffers out
below the loan: the startup chunk if it fits there, else the usual sizes below it (12288 -> 8192, not 6144). RECLAIM
is refused while the VRAM is not free yet (the borrower still exiting): under WDDM an over-full card pages instead of
failing.
The server: --vision-on-demand ("vision_on_demand": true in the config; it adds --lendable-cache) starts the image
encoder with the first picture the cache does not hold, after a LEND of --vision-lend-mib ("vision_lend_mib", default
1400), keeps it while pictures keep coming, and unloads it --vision-idle seconds ("vision_idle_s", default 600) after
the last one - between requests only - then RECLAIMs. An engine restart or unload ends the loan; an engine that cannot
lend keeps the encoder resident as before. With --expert-cache auto the reserve is the one without images: the engine
measures the VRAM that is free once its cache is written, before any encoder is there.
Measured on an RTX 5070 Ti (IQ3_XXS, 400K context, 0.1.34 + Niko1221#439 Niko1221#452 Niko1221#453,
bench/results/2026-10-01-vision-on-demand): decode 59.8 tok/s with the encoder resident, 66.0 on demand before any
picture, 55.4 while it is loaded, 65.2 after it unloads; the first picture 8.3 s to the answer; LEND 0-16 ms, RECLAIM
0.9-1.15 s with every slot verified; the engine's VRAM the same after 5 cycles; with --pcie-frac 0 (byte-identical
repeats) greedy replies after RECLAIM are the replies before LEND.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hile it is loaded (--vision-on-demand; engine --lendable-cache, LEND / RECLAIM)
The engine: --lendable-cache makes the expert cache 64 MiB pieces of one reserved address range (CUDA virtual memory,
looked up through cudaGetDriverEntryPointByVersion, so nothing new is linked). --serve takes two lines between
requests: LEND <MiB> marks the experts in the cache's last MiB as CPU misses, uploads the residency table and unmaps
and frees those pieces (another process can have the VRAM); RECLAIM maps new pieces at the same addresses, refills
every lent slot from the arena (first and last read back; all with STRATA_VERIFY_RECLAIM=1) and marks them resident
again. While the loan is out the adaptive tier leaves the lent experts alone and the prompt path lays its buffers out
below the loan: the startup chunk if it fits there, else the usual sizes below it (12288 -> 8192, not 6144). RECLAIM
is refused while the VRAM is not free yet (the borrower still exiting): under WDDM an over-full card pages instead of
failing.
The server: --vision-on-demand ("vision_on_demand": true in the config; it adds --lendable-cache) starts the image
encoder with the first picture the cache does not hold, after a LEND of --vision-lend-mib ("vision_lend_mib", default
1400), keeps it while pictures keep coming, and unloads it --vision-idle seconds ("vision_idle_s", default 600) after
the last one - between requests only - then RECLAIMs. An engine restart or unload ends the loan; an engine that cannot
lend keeps the encoder resident as before. With --expert-cache auto the reserve is the one without images: the engine
measures the VRAM that is free once its cache is written, before any encoder is there.
Measured on an RTX 5070 Ti (IQ3_XXS, 400K context, 0.1.34 + Niko1221#439 Niko1221#452 Niko1221#453,
bench/results/2026-10-01-vision-on-demand): decode 59.8 tok/s with the encoder resident, 66.0 on demand before any
picture, 55.4 while it is loaded, 65.2 after it unloads; the first picture 8.3 s to the answer; LEND 0-16 ms, RECLAIM
0.9-1.15 s with every slot verified; the engine's VRAM the same after 5 cycles; with --pcie-frac 0 (byte-identical
repeats) greedy replies after RECLAIM are the replies before LEND.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hile it is loaded (--vision-on-demand; engine --lendable-cache, LEND / RECLAIM)
The engine: --lendable-cache makes the expert cache 64 MiB pieces of one reserved address range (CUDA virtual memory,
looked up through cudaGetDriverEntryPointByVersion, so nothing new is linked). --serve takes two lines between
requests: LEND <MiB> marks the experts in the cache's last MiB as CPU misses, uploads the residency table and unmaps
and frees those pieces (another process can have the VRAM); RECLAIM maps new pieces at the same addresses, refills
every lent slot from the arena (first and last read back; all with STRATA_VERIFY_RECLAIM=1) and marks them resident
again. While the loan is out the adaptive tier leaves the lent experts alone and the prompt path lays its buffers out
below the loan: the startup chunk if it fits there, else the usual sizes below it (12288 -> 8192, not 6144). RECLAIM
is refused while the VRAM is not free yet (the borrower still exiting): under WDDM an over-full card pages instead of
failing.
The server: --vision-on-demand ("vision_on_demand": true in the config; it adds --lendable-cache) starts the image
encoder with the first picture the cache does not hold, after a LEND of --vision-lend-mib ("vision_lend_mib", default
1400), keeps it while pictures keep coming, and unloads it --vision-idle seconds ("vision_idle_s", default 600) after
the last one - between requests only - then RECLAIMs. An engine restart or unload ends the loan; an engine that cannot
lend keeps the encoder resident as before. With --expert-cache auto the reserve is the one without images: the engine
measures the VRAM that is free once its cache is written, before any encoder is there.
Measured on an RTX 5070 Ti (IQ3_XXS, 400K context, 0.1.34 + Niko1221#439 Niko1221#452 Niko1221#453,
bench/results/2026-10-01-vision-on-demand): decode 59.8 tok/s with the encoder resident, 66.0 on demand before any
picture, 55.4 while it is loaded, 65.2 after it unloads; the first picture 8.3 s to the answer; LEND 0-16 ms, RECLAIM
0.9-1.15 s with every slot verified; the engine's VRAM the same after 5 cycles; with --pcie-frac 0 (byte-identical
repeats) greedy replies after RECLAIM are the replies before LEND.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hile it is loaded (--vision-on-demand; engine --lendable-cache, LEND / RECLAIM)
The engine: --lendable-cache makes the expert cache 64 MiB pieces of one reserved address range (CUDA virtual memory,
looked up through cudaGetDriverEntryPointByVersion, so nothing new is linked). --serve takes two lines between
requests: LEND <MiB> marks the experts in the cache's last MiB as CPU misses, uploads the residency table and unmaps
and frees those pieces (another process can have the VRAM); RECLAIM maps new pieces at the same addresses, refills
every lent slot from the arena (first and last read back; all with STRATA_VERIFY_RECLAIM=1) and marks them resident
again. While the loan is out the adaptive tier leaves the lent experts alone and the prompt path lays its buffers out
below the loan: the startup chunk if it fits there, else the usual sizes below it (12288 -> 8192, not 6144). RECLAIM
is refused while the VRAM is not free yet (the borrower still exiting): under WDDM an over-full card pages instead of
failing.
The server: --vision-on-demand ("vision_on_demand": true in the config; it adds --lendable-cache) starts the image
encoder with the first picture the cache does not hold, after a LEND of --vision-lend-mib ("vision_lend_mib", default
1400), keeps it while pictures keep coming, and unloads it --vision-idle seconds ("vision_idle_s", default 600) after
the last one - between requests only - then RECLAIMs. An engine restart or unload ends the loan; an engine that cannot
lend keeps the encoder resident as before. With --expert-cache auto the reserve is the one without images: the engine
measures the VRAM that is free once its cache is written, before any encoder is there.
Measured on an RTX 5070 Ti (IQ3_XXS, 400K context, 0.1.34 + Niko1221#439 Niko1221#452 Niko1221#453,
bench/results/2026-10-01-vision-on-demand): decode 59.8 tok/s with the encoder resident, 66.0 on demand before any
picture, 55.4 while it is loaded, 65.2 after it unloads; the first picture 8.3 s to the answer; LEND 0-16 ms, RECLAIM
0.9-1.15 s with every slot verified; the engine's VRAM the same after 5 cycles; with --pcie-frac 0 (byte-identical
repeats) greedy replies after RECLAIM are the replies before LEND.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hile it is loaded (--vision-on-demand; engine --lendable-cache, LEND / RECLAIM)
The engine: --lendable-cache makes the expert cache 64 MiB pieces of one reserved address range (CUDA virtual memory,
looked up through cudaGetDriverEntryPointByVersion, so nothing new is linked). --serve takes two lines between
requests: LEND <MiB> marks the experts in the cache's last MiB as CPU misses, uploads the residency table and unmaps
and frees those pieces (another process can have the VRAM); RECLAIM maps new pieces at the same addresses, refills
every lent slot from the arena (first and last read back; all with STRATA_VERIFY_RECLAIM=1) and marks them resident
again. While the loan is out the adaptive tier leaves the lent experts alone and the prompt path lays its buffers out
below the loan: the startup chunk if it fits there, else the usual sizes below it (12288 -> 8192, not 6144). RECLAIM
is refused while the VRAM is not free yet (the borrower still exiting): under WDDM an over-full card pages instead of
failing.
The server: --vision-on-demand ("vision_on_demand": true in the config; it adds --lendable-cache) starts the image
encoder with the first picture the cache does not hold, after a LEND of --vision-lend-mib ("vision_lend_mib", default
1400), keeps it while pictures keep coming, and unloads it --vision-idle seconds ("vision_idle_s", default 600) after
the last one - between requests only - then RECLAIMs. An engine restart or unload ends the loan; an engine that cannot
lend keeps the encoder resident as before. With --expert-cache auto the reserve is the one without images: the engine
measures the VRAM that is free once its cache is written, before any encoder is there.
Measured on an RTX 5070 Ti (IQ3_XXS, 400K context, 0.1.34 + Niko1221#439 Niko1221#452 Niko1221#453,
bench/results/2026-10-01-vision-on-demand): decode 59.8 tok/s with the encoder resident, 66.0 on demand before any
picture, 55.4 while it is loaded, 65.2 after it unloads; the first picture 8.3 s to the answer; LEND 0-16 ms, RECLAIM
0.9-1.15 s with every slot verified; the engine's VRAM the same after 5 cycles; with --pcie-frac 0 (byte-identical
repeats) greedy replies after RECLAIM are the replies before LEND.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4cda6ba to
e71abf8
Compare
On Turing mode 4 would run as pairs of m16n8k8 MMAs, which no parity run has checked yet; RTX 20 cards keep the old kernel for --kv q4_0 until one does. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0154bwiad8gY7wRehCXy27pN
|
Released in 0.1.38: |
…hile it is loaded (--vision-on-demand; engine --lendable-cache, LEND / RECLAIM)
The engine: --lendable-cache makes the expert cache 64 MiB pieces of one reserved address range (CUDA virtual memory,
looked up through cudaGetDriverEntryPointByVersion, so nothing new is linked). --serve takes two lines between
requests: LEND <MiB> marks the experts in the cache's last MiB as CPU misses, uploads the residency table and unmaps
and frees those pieces (another process can have the VRAM); RECLAIM maps new pieces at the same addresses, refills
every lent slot from the arena (first and last read back; all with STRATA_VERIFY_RECLAIM=1) and marks them resident
again. While the loan is out the adaptive tier leaves the lent experts alone and the prompt path lays its buffers out
below the loan: the startup chunk if it fits there, else the usual sizes below it (12288 -> 8192, not 6144). RECLAIM
is refused while the VRAM is not free yet (the borrower still exiting): under WDDM an over-full card pages instead of
failing.
The server: --vision-on-demand ("vision_on_demand": true in the config; it adds --lendable-cache) starts the image
encoder with the first picture the cache does not hold, after a LEND of --vision-lend-mib ("vision_lend_mib", default
1400), keeps it while pictures keep coming, and unloads it --vision-idle seconds ("vision_idle_s", default 600) after
the last one - between requests only - then RECLAIMs. An engine restart or unload ends the loan; an engine that cannot
lend keeps the encoder resident as before. With --expert-cache auto the reserve is the one without images: the engine
measures the VRAM that is free once its cache is written, before any encoder is there.
Measured on an RTX 5070 Ti (IQ3_XXS, 400K context, 0.1.34 + Niko1221#439 Niko1221#452 Niko1221#453,
bench/results/2026-10-01-vision-on-demand): decode 59.8 tok/s with the encoder resident, 66.0 on demand before any
picture, 55.4 while it is loaded, 65.2 after it unloads; the first picture 8.3 s to the answer; LEND 0-16 ms, RECLAIM
0.9-1.15 s with every slot verified; the engine's VRAM the same after 5 cycles; with --pcie-frac 0 (byte-identical
repeats) greedy replies after RECLAIM are the replies before LEND.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 99e8b36)
…iko1221#452 Niko1221#453, Niko1221#439 as Niko1221#372) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hile it is loaded (--vision-on-demand; engine --lendable-cache, LEND / RECLAIM)
The engine: --lendable-cache makes the expert cache 64 MiB pieces of one reserved address range (CUDA virtual memory,
looked up through cudaGetDriverEntryPointByVersion, so nothing new is linked). --serve takes two lines between
requests: LEND <MiB> marks the experts in the cache's last MiB as CPU misses, uploads the residency table and unmaps
and frees those pieces (another process can have the VRAM); RECLAIM maps new pieces at the same addresses, refills
every lent slot from the arena (first and last read back; all with STRATA_VERIFY_RECLAIM=1) and marks them resident
again. While the loan is out the adaptive tier leaves the lent experts alone and the prompt path lays its buffers out
below the loan: the startup chunk if it fits there, else the usual sizes below it (12288 -> 8192, not 6144). RECLAIM
is refused while the VRAM is not free yet (the borrower still exiting): under WDDM an over-full card pages instead of
failing.
The server: --vision-on-demand ("vision_on_demand": true in the config; it adds --lendable-cache) starts the image
encoder with the first picture the cache does not hold, after a LEND of --vision-lend-mib ("vision_lend_mib", default
1400), keeps it while pictures keep coming, and unloads it --vision-idle seconds ("vision_idle_s", default 600) after
the last one - between requests only - then RECLAIMs. An engine restart or unload ends the loan; an engine that cannot
lend keeps the encoder resident as before. With --expert-cache auto the reserve is the one without images: the engine
measures the VRAM that is free once its cache is written, before any encoder is there.
Measured on an RTX 5070 Ti (IQ3_XXS, 400K context, 0.1.34 + Niko1221#439 Niko1221#452 Niko1221#453,
bench/results/2026-10-01-vision-on-demand): decode 59.8 tok/s with the encoder resident, 66.0 on demand before any
picture, 55.4 while it is loaded, 65.2 after it unloads; the first picture 8.3 s to the answer; LEND 0-16 ms, RECLAIM
0.9-1.15 s with every slot verified; the engine's VRAM the same after 5 cycles; with --pcie-frac 0 (byte-identical
repeats) greedy replies after RECLAIM are the replies before LEND.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 99e8b36)
Upstream v0.1.38: faster prompts (one gather per expert group Niko1221#372, the first chunk's PLE rows beside layer 0 Niko1221#374, DeltaNet three heads per thread Niko1221#413), --kv q4_0 prompts on tensor cores Niko1221#452, Q5_0 experts on the GPU Niko1221#473, IQ4_XS on AVX-2 Niko1221#415, unbuffered expert loading on Windows Niko1221#357/Niko1221#362, the --peer-device second-GPU expert tier Niko1221#531, a 6 GB card starts Niko1221#496, the Host header / DNS-rebinding checks, PR batch. Our side: the AVX1 floor (STRATA_ISA_FLOOR, cpu_avx1_ok, the relaxed gate, the scalar router fallback), the AVX1 router kernel (kq_avx1.cpp), the retune patch + setup hooks.
On Turing mode 4 would run as pairs of m16n8k8 MMAs, which no parity run has checked yet; RTX 20 cards keep the old kernel for --kv q4_0 until one does.
On Turing mode 4 would run as pairs of m16n8k8 MMAs, which no parity run has checked yet; RTX 20 cards keep the old kernel for --kv q4_0 until one does.
…iko1221#452), Q5_0 experts on the GPU (Niko1221#473), IQ4_XS on AVX-2 (Niko1221#415), unbuffered loads on Windows (Niko1221#357 Niko1221#362), --peer-device (Niko1221#531), a 6 GB card starts (Niko1221#496), PR batch; setup requires it
On Turing mode 4 would run as pairs of m16n8k8 MMAs, which no parity run has checked yet; RTX 20 cards keep the old kernel for --kv q4_0 until one does. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0154bwiad8gY7wRehCXy27pN
…iko1221#452), Q5_0 experts on the GPU (Niko1221#473), IQ4_XS on AVX-2 (Niko1221#415), unbuffered loads on Windows (Niko1221#357 Niko1221#362), --peer-device (Niko1221#531), a 6 GB card starts (Niko1221#496), PR batch; setup requires it Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0154bwiad8gY7wRehCXy27pN
What
The tensor-core prompt attention (D-1,
qsa_prompt_attn_batch) handles FP16, INT8 and K8V4 KV but refuses Q4_0. With--kv q4_0, every prompt's QSA attention therefore ran on the FP32 per-query kernel (qsa_decode_attn_batch, 32queries at a time). This adds a mode for Q4_0 K and V.
(code - 8) at gather, and its fp16 scale goes into the per-group scales: 8 groups of 32 per row, where int8 has 4
of 64. The scales are applied in FP32 as int8's are: q.k per 32-dim group, and for p.v each group's scale is
folded into p (two groups per warp).
d = max / -8, so about half of a real pool's blocks have a negative scale.The p.v fold is bounded by the chunk's largest
|scale|.and of the output is unchanged.
STRATA_PROMPT_ATTN_Q4=0restores the old kernel.qsa_prompt_attn_paritygets Q4_0 cases (32K and 1.5K contexts) against FP64 and the old kernel. Theirscales are signed and spread over two decades, as a pool's are.
What it speeds up, and what it does not
--kv q4_0: +18.5% at 8K tokens, +18.8% at 16K, +19.9% at 30K. With the old kernel the attentionwas 21-22% of a long prompt's GPU time on this machine.
the same numbers as before.
Measured
Setup:
--kv q4_0 --kv-resident 32768, text-only args (4,560 expert slots),--prefill auto(8,192-token chunks).
Kernel (
qsa_prompt_attn_parity 32768 2048 5):The int8 and fp16 cases still pass.
Prompts:
STRATA_PROMPT_ATTN_Q4=0) and mode 4 in alternating engine runs (old, new, old, new).both arms by their own variables, so the arms are v0.1.34 and v0.1.34 + this commit.
Quality (not bitwise, as D-1 is not):
tools/needle_bench.py's haystack and needle, driven through the engine. 8K, 16K and 32K at depths10/30/50/70/90: 15 of 15 with the old kernel, 15 of 15 with mode 4.
model amplifies any FP32-level change.
bounded. On real K/V that overflowed FP16 and the model returned NaN (needles 0 of 15). The parity test had passed
it, because its synthetic scales were all positive. The test's scales are now signed, and it fails on that bound
(new vs old 0.93 of the output scale).
Not tested here
Developed with an AI coding assistant; all numbers measured on the machine above.
🤖 Generated with Claude Code