Repository navigation
Conversation
…n 32-token chunks (opt-in, 1.8x faster, other bits) The gated delta rule in the WY form flash-linear-attention uses, all FP32: gdn_chunk_prep_kernel forms k.k / q.k and T = (I + A)^-1 for every chunk of a 2048-token super-block at once; gdn_chunk_scan_kernel (128 blocks, a key head x 16 value columns each) walks the chunks with its state slice in registers. Every exponent <= 0 (the gates are negative). The scratch (25.6 MB) is allocated once per device: freed and reallocated per call it cost more host time than the kernels saved on short prompts. sm_80+, >= 128 SMs, enough shared memory, chunks of >= 128 tokens; else gdn_rec_kh_kernel. Unset: unchanged. gdn_rec_parity: against gdn_rec_kh_kernel <= 1.13e-6 (T = 1..4099, 8192, 32768, a non-zero starting state); against an FP64 recurrence <= 1.05e-6 (the sequential kernel <= 3.2e-7) - FP32 rounding; 199 restarts of 7 shapes bit for bit (NaN-filled scratch between). RTX 5090: 1.81x the recurrence at 2K-32K; IQ2_XS: the recurrence phase of a 32K prompt 520-532 -> 306-334 ms (-210 ms, -3.7%), 8K -2.1..-3.6%; first-token KL 0.0016-0.0152, 1.15-1.83x a harmless control arm (STRATA_PROMPT_ATTN_V1), the same top token. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio
|
Context from the AMD side (we maintain the gfx1100 HIP port, #1292) — data points, not a review of the CUDA design:
|
…ED=1 - the prompt's recurrence in 32-token chunks (opt-in, 1.8x faster, other bits) DeltaNet recurrence: STRATA_GDN_CHUNKED=1 - the prompt's recurrence in 32-token chunks (opt-in, 1.8x faster, other bits) The gated delta rule in the WY form flash-linear-attention uses, all FP32: gdn_chunk_prep_kernel forms k.k / q.k and T = (I + A)^-1 for every chunk of a 2048-token super-block at once; gdn_chunk_scan_kernel (128 blocks, a key head x 16 value columns each) walks the chunks with its state slice in registers. Every exponent <= 0 (the gates are negative). The scratch (25.6 MB) is allocated once per device: freed and reallocated per call it cost more host time than the kernels saved on short prompts. sm_80+, >= 128 SMs, enough shared memory, chunks of >= 128 tokens; else gdn_rec_kh_kernel. Unset: unchanged. gdn_rec_parity: against gdn_rec_kh_kernel <= 1.13e-6 (T = 1..4099, 8192, 32768, a non-zero starting state); against an FP64 recurrence <= 1.05e-6 (the sequential kernel <= 3.2e-7) - FP32 rounding; 199 restarts of 7 shapes bit for bit (NaN-filled scratch between). RTX 5090: 1.81x the recurrence at 2K-32K; IQ2_XS: the recurrence phase of a 32K prompt 520-532 -> 306-334 ms (-210 ms, -3.7%), 8K -2.1..-3.6%; first-token KL 0.0016-0.0152, 1.15-1.83x a harmless control arm (STRATA_PROMPT_ATTN_V1), the same top token. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0157fjixQD2nRxvCvtXq66DD
|
Thanks @sergqwer. Ported with your authorship, opt-in as you wrote it ( We have no card with 128 SMs, so we added Closing as ported. |
…tch-audit.py + fetched PR diffs) Line-level audit of the eight patches applied on tuned-1403 against the tuned-141 tree and against upstream 0.1.41: Niko1221#1323 and Niko1221#1372 are upstream's now, Niko1221#1381/Niko1221#1439/Niko1221#1451 are re-applied on 141, while Niko1221#1416 (CPU share), Niko1221#1448 (q8_1 finite-ds) and Niko1221#1457 (HC scratch reuse) have no counterpart there. patch-audit.py compares a diff's added code lines against a tree, which survives the rebases and reworkings that defeat 'git apply -R --check'.
…-support Upstream now carries these of our picks, in its own versions: Niko1221#1237 (pinned stage buffers, now with fences and the STRATA_STAGE_PIN switch), Niko1221#1332, Niko1221#1359, Niko1221#1372, Niko1221#1370, Niko1221#1251, Niko1221#1190 and Niko1221#1283. CPU help for short prompts: Strata's own CPU prefill share (0.1.41: on by default below 1,024 tokens on one NVIDIA GPU; -20..-34% prompt time at 512-1,000 tokens on an RTX 3060 in upstream's measurement) replaces architectds' CPU assist, which measured +11% at ~560 tokens on our desktop. prefill.cpp is upstream's, with the pool's hooks (remote_next to the workers, a headless worker stage) and Niko1221#1368's quantize-once put back. A pool PC runs its own layers alone (no local stages), so the share's default applies on every PC. Pipelined decode: upstream's N-stage loop (front stages + the last) with the pool's remote stage as the last (pl_remote via stage_ver_p, pl_ns = 2 on a pool coordinator), and architectds' verified-path-first yield, the lookup carried across windows and the pool head's penalty rows ported onto it. Batch: upstream's batch_step (the adaptive tier in batch windows, per-stage timing, Niko1221#879 non-finite logits) with Niko1221#1290's per-slot row width and the pool head's slot histories; the pool's batch groups keep their PoolLink stage. Also: the conversation cache's eviction keeps both the pool-borrowed entry and pinned prefixes (evict_oldest); the verifier's adaptive-tier gate (STRATA_ADAPT_OVERLAP) also guards the overlapped split's prelaunch (Niko1221#1120); the server's read-once body takes upstream's size limit; replicas and the pool roles both start engines; serve/pool.py keeps the engine's newer spawn tuple; the worker's parking reads available_host_bytes(); setup tests that need a ready-made engine are skipped (StrataPool compiles its own). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0157fjixQD2nRxvCvtXq66DD
…nce in 16-token chunks on the matrix cores (opt-in) The switch exists for CUDA (Niko1221#1372) and is compiled out on HIP. On gfx11 it now runs the WY form of the gated delta rule in chunks of 16 tokens, every product one 16x16x16 WMMA (FP16 operands, FP32 accumulation, the state in the accumulators), with the output norm and the z gate fused: a prep kernel for T and P of every chunk, then a scan with a block per value head. It rounds differently from the FP32 recurrence, so it is opt-in; the default path is unchanged. A gfx11 part, 64 tokens and more, an output row stride divisible by 8 and no STRATA_GDN_REC_HEADS take it; anything else runs the existing kernels. tests/hip/gdn_chunked.cpp: bounds on the FP16 output and the carried state against the existing recurrence over 0-16,385 tokens (chunk and stride edges, a split request), a double-precision CPU check, and bit-identical output with the switch off or unset. tests/hip/gdn_rec_head.cpp unsets the switch, as its variants 0 and 1 are the references there. gfx1151 (Ryzen AI Max+ 395, UD-IQ4_XS): a layer's recurrence + norm takes 1.77x less time at 16K tokens and 1.81x at 32K; a whole prompt about 1-2% less at 32K-128K (2.2-2.3% with the docs/STRIX_HALO.md section-5 switches, 3 pairs of 3 faster; 0.9-1.1% without them, 2 of 3), no gain to rely on at 4K; first-token KL against the default 0.006-0.007 on average, the same top token in 12 of 12 prompts. Run on gfx1151 only; kernels.cu compiles for gfx1030, 1100, 1151, 1152 and 1201 (docs/AMD_HIP.md). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nce in 16-token chunks on the matrix cores (opt-in) The switch exists for CUDA (Niko1221#1372) and is compiled out on HIP. On gfx11 it now runs the WY form of the gated delta rule in chunks of 16 tokens, every product one 16x16x16 WMMA (FP16 operands, FP32 accumulation, the state in the accumulators), with the output norm and the z gate fused: a prep kernel for T and P of every chunk, then a scan with a block per value head. It rounds differently from the FP32 recurrence, so it is opt-in; the default path is unchanged. A gfx1100, gfx1101, gfx1102, gfx1150 or gfx1151 part, 64 tokens and more, an output row stride divisible by 8 and no STRATA_GDN_REC_HEADS take it; anything else runs the existing kernels. tests/hip/gdn_chunked.cpp: bounds on the FP16 output and the carried state against the existing recurrence over 0-16,385 tokens (chunk and stride edges, a split request), a double-precision CPU check, and bit-identical output with the switch off or unset. tests/hip/gdn_rec_head.cpp unsets the switch, as its variants 0 and 1 are the references there. hip_gdn_chunked --bench and --bench-default time this path and gfx1151's default kernels, one per process (the switch is read once). gfx1151 (Ryzen AI Max+ 395, UD-IQ4_XS): a layer's recurrence + norm takes 1.60x less time than the default kernels at 16K and 32K tokens; a whole prompt 1.4-2.1% less at 4K-128K after a warm-up prompt (11 pairs each: 1.8-2.1% with the docs/STRIX_HALO.md section-5 switches, 1.4-1.5% without them, 10 or 11 of 11 faster); first-token KL against the default 0.006-0.007 on average, the same top token in 12 of 12 prompts. Run on gfx1151 only; kernels.cu compiles for gfx1030, 1100, 1151, 1152 and 1201 (docs/AMD_HIP.md). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nce in 16-token chunks on the matrix cores (opt-in) The switch exists for CUDA (Niko1221#1372) and is compiled out on HIP. On gfx11 it now runs the WY form of the gated delta rule in chunks of 16 tokens, every product one 16x16x16 WMMA (FP16 operands, FP32 accumulation, the state in the accumulators), with the output norm and the z gate fused: a prep kernel for T and P of every chunk, then a scan with a block per value head. It rounds differently from the FP32 recurrence, so it is opt-in; the default path is unchanged. A gfx1100, gfx1101, gfx1102, gfx1103, gfx1150 or gfx1151 part, 64 tokens and more, an output row stride divisible by 8 and no STRATA_GDN_REC_HEADS take it; anything else runs the existing kernels. tests/hip/gdn_chunked.cpp: bounds on the FP16 output and the carried state against the existing recurrence over 0-16,385 tokens (chunk and stride edges, a split request), a double-precision CPU check, and bit-identical output with the switch off or unset. tests/hip/gdn_rec_head.cpp unsets the switch, as its variants 0 and 1 are the references there. hip_gdn_chunked --bench and --bench-default time this path and gfx1151's default kernels, one per process (the switch is read once). gfx1151 (Ryzen AI Max+ 395, UD-IQ4_XS): a layer's recurrence + norm takes 1.6x less time than the default kernels at 16K and 32K tokens; a whole prompt 1.1-2.5% less at 4K-128K after a warm-up prompt (11 pairs each: 1.9-2.5% with the docs/STRIX_HALO.md section-5 switches, 1.1-1.2% without them, 10 or 11 of 11 faster); first-token KL against the default 0.006-0.007 on average, the same top token in 12 of 12 prompts. Run on gfx1151 only; kernels.cu compiles for gfx1030, 1100, 1103, 1151, 1152 and 1201 (docs/AMD_HIP.md). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nce in 16-token chunks on the matrix cores (opt-in) The switch exists for CUDA (#1372) and is compiled out on HIP. On gfx11 it now runs the WY form of the gated delta rule in chunks of 16 tokens, every product one 16x16x16 WMMA (FP16 operands, FP32 accumulation, the state in the accumulators), with the output norm and the z gate fused: a prep kernel for T and P of every chunk, then a scan with a block per value head. It rounds differently from the FP32 recurrence, so it is opt-in; the default path is unchanged. A gfx1100, gfx1101, gfx1102, gfx1150 or gfx1151 part, 64 tokens and more, an output row stride divisible by 8 and no STRATA_GDN_REC_HEADS take it; anything else runs the existing kernels. tests/hip/gdn_chunked.cpp: bounds on the FP16 output and the carried state against the existing recurrence over 0-16,385 tokens (chunk and stride edges, a split request), a double-precision CPU check, and bit-identical output with the switch off or unset. tests/hip/gdn_rec_head.cpp unsets the switch, as its variants 0 and 1 are the references there. hip_gdn_chunked --bench and --bench-default time this path and gfx1151's default kernels, one per process (the switch is read once). gfx1151 (Ryzen AI Max+ 395, UD-IQ4_XS): a layer's recurrence + norm takes 1.60x less time than the default kernels at 16K and 32K tokens; a whole prompt 1.4-2.1% less at 4K-128K after a warm-up prompt (11 pairs each: 1.8-2.1% with the docs/STRIX_HALO.md section-5 switches, 1.4-1.5% without them, 10 or 11 of 11 faster); first-token KL against the default 0.006-0.007 on average, the same top token in 12 of 12 prompts. Run on gfx1151 only; kernels.cu compiles for gfx1030, 1100, 1151, 1152 and 1201 (docs/AMD_HIP.md). Co-authored-by: libratechw <libratechwriter@gmail.com>
…d path from 128 tokens by default PR Niko1221#1372's fix: one cudaMalloc of the 25.6 MB scratch per device on first use and the kernels' attributes set once, instead of a stream-ordered alloc / free and cudaFuncSetAttribute on every call - on Windows that host time made the chunked path lose on short chunks (a 2K prompt ~35 ms slower), which is why the default started at 16384 tokens. The chunked output is the same bits as before (2K / 32K logits and tokens with STRATA_GDN_CHUNKED=1 unchanged). Now the recurrence phase wins at every size (tray model: 600 tokens 9 -> 6 ms, 2K 32-33 -> 21-22, 8K 123-129 -> 82-88, 32K 514-515 -> 310-328), and a quiet 2K A/B: 1044 / 1039 / 1035 against 1064 / 1044 / 1047 ms - so the default is the chunked recurrence from 128 tokens (=0: never; =1: the same). ctest 87/90, gdn_rec_parity 0 failures; the 200-token determinism hash changes (2K now takes the chunked path). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio
…n the 0.1.41 port's conflict resolution) The port put the fork's kh3 code before upstream's chunked recurrence (Niko1221#1372, 0f78032) in kernels.cu and gdn_rec_parity.cu; the brace both sides shared stayed after the chunked code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio
The prompt path's DeltaNet recurrence (
gdn_rec_kh_kernel) walks a chunk one token at a time. On an RTX 5090 it costs ~0.41 µs per token and layer: 520-530 ms of a 32K prompt (9%). This PR adds an opt-in (STRATA_GDN_CHUNKED=1) that computes the recurrence in chunks of 32 tokens, in the WY form flash-linear-attention uses, all in FP32. The kernel is 1.81x faster per launch, and it takes ~210 ms (3.7%) off a 32K prompt. It sums in another order than the sequential kernel, so its bits differ; that is why it is opt-in. Unset (the default), nothing changes.What it does
The math, per value head, with γ_t the gate's running sum inside the chunk and S0 the state before it:
The two kernels:
gdn_chunk_prep_kernel, one block per (chunk, key head), for every chunk of a 2048-token super-block at once. It forms k·k and q·k and solves for T (one lane per token). It writes T, P = e^(γ_t − γ_i) q_t·k_i and γ for the key head's three value heads.gdn_chunk_scan_kernel, one block per (key head, 16 value columns): 128 blocks. Each block walks the super-block's chunks with its state slice in registers. The products are register-tiled from shared memory, and the next chunk's k, q, v, γ and β load (cp.async) while the current one computes.Scratch. The prep's output for a super-block takes 25.6 MB. It is allocated once per device, on the first call, and kept; the two kernels' shared-memory attributes are also set once. A stream-ordered allocation and free per call cost more host time than the kernels saved: a 2K prompt's recurrence took 78 ms against 34 ms.
Where it runs. Only on prompt chunks of ≥ 128 tokens (
kGdnChunkedMin; see below), on a CUDA card with:STRATA_EMULATE_CCis honoured. Anywhere else the existing kernels run: smaller cards (RTX 3090, 4080, 5080), a failed scratch allocation, and HIP/SYCL, where none of this code is compiled.Builds and tests.
kernels.cucompiles for 75/80/86/89/120.gdn_rec_paritynow:gdn_rec_kh_kernel(fails above 1e-4 relative) at T = 1..4099, 8192 and 32768, from a non-zero state;--bench).Measured
RTX 5090 (170 SMs), Ryzen 9 9950X3D, IQ2_XS (ISTA GSQ-RCO),
--kv int8,--expert-cache 12000.Kernel accuracy (
gdn_rec_parity, max |diff| / max |value|, output / state after the chunk, 0 failures):gdn_rec_kh_kernelgdn_rec_kh_kernelEnd to end, first-token KL to the default. The noise arm is
STRATA_PROMPT_ATTN_V1=1: upstream's first prompt-attention kernel, which has the same accuracy but sums in another FP32 order. The last column says whether the 32 greedy tokens match the default's.The switch moves the output by 1.15-1.83x what the noise arm does. The top token is the same everywhere. The greedy tokens match the default's on 3 of 4 prompts with the switch, and on 1 of 4 with the noise arm.
Speed per launch (
gdn_rec_parity --bench, one layer, 48 value heads, medians of alternating runs):gdn_rec_kh_kernelBelow ~100 tokens the two launches cost more than they save, so the threshold is 128 tokens. Since the switch is opt-in, it then applies to nearly every prompt chunk.
In the engine, the recurrence phase (
STRATA_PREFILL_TIMING=1, 3 to 6 runs each):Prompt time, 3 interleaved pairs in each order:
Identity. With the switch unset, the first-token logits and the 32 greedy tokens equal v0.1.40.2 on IQ2_XS after 2K, 8K and 32K (identical bytes; 2K repeated on the final build).
Our NVFP4 fork has shipped these kernels since 0.1.40-nvfp4.3. This PR adds the per-device scratch, which removes the per-call cost that had kept the fork's threshold at 16K tokens.
🤖 Generated with Claude Code
https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio