Repository navigation
Conversation
…NT8 tensor cores (opt-in, closer to FP32) prompt_attn_i8v3_kernel: QK^T and PV as mma m16n8k32 on the int8 K / V codes as stored; the other operand in three exact 8-bit parts (q scaled per head and 64-dim group, p * v_scale per row and chunk; X = rint(x * 65536) split into h, l signed and m unsigned bytes, a u8 x s8 MMA): 24 bits, exact int32 sums, combined in FP32. One K and one V stage per warp, 12-row partials, launch bounds (128, 3): ~25.5 KB, 3 blocks an SM. sm_80+ (STRATA_PA_SM80); Turing, Volta and HIP keep their kernels. Unset: v2, byte-identical. qsa_prompt_attn_parity: against FP64 IMMA 2.15e-6 - 2.64e-6, v2 2.78e-6 - 3.30e-6, the FP32 kernel 1.90e-6 - 2.51e-6. IQ2_XS, first-token KL to the FP32 kernel (STRATA_PROMPT_ATTN_OLD=1): median 0.0027 (IMMA) against 0.013 (v2); 2K 0.0026 against 0.0248. 1.35-1.53x a launch; a 32K prompt in 8K chunks 358 -> 312 ms of attention. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio
|
A datapoint from our int8 attention work on gfx1100 (fork If AMD coverage is ever wanted: the RDNA3 primitive exists (
|
…IMMA=1 - the int8-KV kernel on INT8 tensor cores (opt-in, closer to FP32) prompt attention: STRATA_PROMPT_ATTN_IMMA=1 - the int8-KV kernel on INT8 tensor cores (opt-in, closer to FP32) prompt_attn_i8v3_kernel: QK^T and PV as mma m16n8k32 on the int8 K / V codes as stored; the other operand in three exact 8-bit parts (q scaled per head and 64-dim group, p * v_scale per row and chunk; X = rint(x * 65536) split into h, l signed and m unsigned bytes, a u8 x s8 MMA): 24 bits, exact int32 sums, combined in FP32. One K and one V stage per warp, 12-row partials, launch bounds (128, 3): ~25.5 KB, 3 blocks an SM. sm_80+ (STRATA_PA_SM80); Turing, Volta and HIP keep their kernels. Unset: v2, byte-identical. qsa_prompt_attn_parity: against FP64 IMMA 2.15e-6 - 2.64e-6, v2 2.78e-6 - 3.30e-6, the FP32 kernel 1.90e-6 - 2.51e-6. IQ2_XS, first-token KL to the FP32 kernel (STRATA_PROMPT_ATTN_OLD=1): median 0.0027 (IMMA) against 0.013 (v2); 2K 0.0026 against 0.0248. 1.35-1.53x a launch; a 32K prompt in 8K chunks 358 -> 312 ms of attention. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0157fjixQD2nRxvCvtXq66DD
|
@xyzzing "Happy to measure on a 7900 XTX if there's interest.". I have a 7900xt (linux) here and run with 70-100K prompts (letta-code). I'd be interested to see if this helps. |
|
Tested on 4× RTX 5080 (sm_120, 84 SMs, PCIe Gen3), IQ3_S,
No speed gain on this rig: on a 4-stage split the prompt time is dominated by expert streaming and the stage hand-offs, not by prompt attention. The changed greedy answers are expected, since it's a different (more accurate) kernel. I can't measure the KL to FP32 here; all answers read fine. It stays off in our production for now. If a long-context quality check would be useful (e.g. 64K-128K with int8 KV), I can run one. |
|
@blange48 thank you for running it on a split rig. Two notes, and the long-context check you offered.
Long context, against the FP32 kernel (
At 96K IMMA is 10x closer to the FP32 kernel than v2, as it was at 2K (0.0026 against 0.025). Greedy answers that part after a few hundred characters are what any FP32-level change does over 220 tokens, and they do not say which kernel is closer. Against the FP32 kernel, IMMA is the closer of the two. |
|
Tested a local v0.1.40.2 port on a single RTX 4070 Laptop (sm_89), Ryzen 9 7940HS, 64 GB RAM, Windows/MSVC and CUDA 13.4. IQ3_S, 64K context, int8 KV with 32K resident on the GPU. The parity benchmark passed and IMMA was closer to FP64 at both main shapes:
Those are gains over v2, not the larger v1-to-v2 gains also printed by the benchmark. Both invocations ended with zero failures. Whole prompt processing was essentially unchanged: 73.605 s with v2 versus 73.352 s with IMMA for 22,305 tokens (two samples per arm). The smaller 635/5,914-token prompts were also close. Same compiled binary, only IMMA toggled; scatter was held off in both arms. All bounded lookup, structured tool-call, cache-return and queue-trace checks passed. This was sampled high-reasoning generation, not a greedy-token identity or model-level KL comparison. So this adds sm_89 parity/per-kernel evidence, but I would not claim an end-to-end speed improvement on this RAM/PCIe-heavy setup from these samples. |
|
Thanks, that settles it. Here are the
So the kernel itself is about a third faster on the 5080 too. On this split the whole prompt doesn't move, as you expected: hand-offs and expert streaming dominate. You're also right about decode: with IMMA on, a later run gave 1 / 2 / 3 / 4 / 8 clients at 145 / 171 / 239 / 268 / 394 tok/s, the same as without it, and my earlier 270 → 240 was variance. Given your 96K KL numbers (10× closer to FP32 than v2), we turned it on in production ( |
|
@blange48 @midhatn thank you both. For the maintainer, the evidence so far on three cards:
Decode never calls the kernel. It stays opt-in ( |
…n on INT8 tensor cores (STRATA_PROMPT_ATTN_IMMA=1)
…): decode STRATA_PCIE_BALANCE=1 (refile of Niko1221#1038)
|
Thanks. Ported as an opt-in (STRATA_PROMPT_ATTN_IMMA=1). On an RTX 3060 the 20K-token prompt read is +2.5% (5 of 5 pairs), teacher-forced KL against the default is at the noise floor (0.0012) and 3 of 3 needles at 32K were found. We want one more card class and a long-prompt KL before making it the default. |
Upstream 0.1.45 took in ten of the PRs we carried: Niko1221#567 (prompt encoder), Niko1221#1164 (sibling borrow), Niko1221#1166, Niko1221#1368, Niko1221#1367, Niko1221#1120 (split overlap), Niko1221#1912, Niko1221#1888, Niko1221#1949, Niko1221#1305. Their versions are kept and our copies dropped: verify.cpp's duplicate prelaunch/handoff block, verify_kernels.hpp, the qsa prompt kernels, gr_upmix_parity, the tokenizer and its tests (tools/test_strata_tokenizer_upstream.py is gone: upstream's test file is the one now), the second quantize_scatter declaration. README credits updated. Pool parts kept on top: - conversation cache: upstream renamed the borrow guard (pin -> guard); clear() resets it. - prefill: the remote_next hand-off beside upstream's hand-off pieces/worker (a pool stage has no pieces: the whole chunk goes to the workers). - generate.cpp: --pool-affinity measure over a node's own layers; the node's dense-range load beside STRATA_HC_REQ8; the carve on primary_device over the pool range; checkpoints keep pool_id (STRATA_CKPT_POOL/THREADS' parallel save is skipped on a pool PC); the workers' retain after eviction and after a resume; the borrow/restore of parked conversations on the workers with upstream's --conversation-cache-park-gain; Niko1221#652's emit count in upstream's merged windows. - expert_source: our budget retry beside upstream's lend budget. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0157fjixQD2nRxvCvtXq66DD
…e (v0.1.45 + Niko1221#1362 + /v1/models context_window); upstream absorbed Niko1221#1107/Niko1221#1367/Niko1221#1548
|
Thanks for the pull request. Your change is already in If part of your work did not make it in and you want it considered, open a new pull request on top of |
The int8-KV prompt attention (
prompt_attn_i8_kernel, v2) drifts away from the FP32 kernel on short prompts. On IQ2_XS the first-token KL to the FP32 kernel is 0.025 after a 2K prompt, and the 32 greedy tokens change. This PR adds an opt-in kernel (STRATA_PROMPT_ATTN_IMMA=1) that puts both products on INT8 tensor cores with an operand split exact to 24 bits. It is closer to FP64 than v2 at every shape tested and 1.35-1.53x faster per launch. Unset (the default), nothing changes.What it does
prompt_attn_i8v3_kernelkeeps v2's block, its cp.async pipeline and its online softmax.Operands. QK^T and PV run as
mma m16n8k32on the int8 K and V codes as stored, with no int8 -> fp16 conversion. The other operand is split into three exact 8-bit parts:u8 x s8MMA).That is 24 bits, finer than v2's FP16 hi + lo split (~22 bits). Each part's int32 sum is exact, and the three parts are combined in FP32.
Occupancy. One K stage and one V stage per warp (K(c+1) is gathered during softmax(c) and PV(c)), 12-row partial sums, and
__launch_bounds__(128, 3): ~25.5 KB of shared memory and 168 registers, so 3 blocks per SM instead of v2's 2. The kernel waits on latency, not on its MMAs: with one block per SM it ran 1.54x slower than with two.Scope. sm_80+ only: the device code is behind the file's
STRATA_PA_SM80, and Turing / Volta / HIP keep their kernels. The file compiles for 75/80/86/89/120. A test hook,qsa_prompt_attn_set_imma(), is added.qsa_prompt_attn_paritynow checks IMMA against FP64 and v2 and times both.Measured
RTX 5090, Ryzen 9 9950X3D, IQ2_XS (ISTA GSQ-RCO),
--kv int8,--expert-cache 12000.Kernel accuracy against FP64 (
qsa_prompt_attn_parity, max relative error, 0 failures):End to end, first-token KL to the FP32 kernel (
STRATA_PROMPT_ATTN_OLD=1); the last column says whether the 32 greedy tokens match the FP32 kernel's:On the same model in NVFP4 (our fork) the gap at 2K was wider: KL 0.10 for v2 against 0.007 for IMMA.
Speed:
Per launch (
qsa_prompt_attn_parity): 32K / 2048 queries 2.09 -> 1.55 ms (1.35x); 1500 / 1500 0.61 -> 0.40 ms (1.53x); 2100 / 256 0.28 -> 0.26 ms (1.09x).In the engine, 32K prompt in 8K chunks (nsys, 48 launches): 358.1 -> 311.6 ms (1.15x).
Prompt time, 3 interleaved pairs, which the machine's noise dominates:
The kernel saves ~47 ms of a ~5 s 32K prompt, which is below that noise.
Identity. With the switch unset, the first-token logits and the 32 greedy tokens equal v0.1.40.2 on IQ2_XS after 2K and after 32K (identical bytes, repeated).
🤖 Generated with Claude Code
https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio