Repository navigation
Conversation
…nded expert uploads (supersedes Niko1221#94) # Conflicts: # CMakeLists.txt # include/strata/platform/direct_file.hpp # src/core/device.cu # src/kernels/cuda/native_qsa_indexer.cu # src/platform/direct_file.cpp # src/prefill/prefill.cpp # src/program/generate.cpp
…end the compat layer Upstream main added TF32/f16 tensor-core QSA scoring, cp.async prompt attention, graph introspection and a GPU timestamp after this PR's base; none of it compiles under HIP. The tensor-core and cp.async kernels stay CUDA-only (the existing warp/old-kernel fallbacks serve HIP, as for pre- sm_80 cards), the graph-node and device-attribute names join the compat defines, math_constants.h gets a compat shim, the stage profiler uses clock64() under HIP, and __trap becomes __builtin_trap. Verified: full HIP build of strata and the CPU build with 8/10 ctests (the two failures need a packed-model fixture; excluded like the PR's own validation).
…aries Hosted runners have no GPUs: the CPU job builds the CPU-only tree and runs its fixture-free tests (including the POSIX direct-file async pool); the CUDA and HIP jobs compile the engine in vendor containers, validated locally against the merged tree. Release builds on v* tags publish the CUDA sm_89 and HIP gfx1100 binaries with checksums.
…complete) rocm/rocm:7.1 does not pull (repository not found); AMD's install guides and the community build on the dev-ubuntu-24.04 -complete images.
- native_mmvq_multi: guard with EXISTS - bench/micro/native_mmvq_multi.cpp is absent from this repository's history and from upstream; the target broke every STRATA_BUILD_TESTS=ON configure. Declared exclusion until the source is added. - qsa_prompt_attn_parity: scope to STRATA_ENABLE_CUDA - it linked CUDA::cudart unconditionally, breaking the HIP configure. The tensor-core prompt-attention path is compiled out under __HIP__, so the test is a declared HIP exclusion. - .agentic/checks.json: configure the project check to run the HIP CTest suite (documented ple_parity/platform_memory_test exclusions plus the Q2_0-fixture-blocked expert_parity/pool_test pair, named in the check). Verified: build-hip-mmq (STRATA_PREFILL_MMQ=ON, STRATA_BUILD_TESTS=ON) builds clean; ctest 36 passed incl. hip_prefill_mmq_parity, 1 documented SKIP (hip_prefill_hipblaslt_gemm), 2 fixture-blocked failures (expert_parity, pool_test: no Q2_0 pack/full fixture on the measuring host).
…ment Measured engine knobs (STRATA_HIPBLASLT_TUNING, STRATA_PF_EMBED_BATCH, STRATA_PREFILL_RING) previously lived only in one systemd unit's command line and vanished on any restart. The config's 'env' now wins over the inherited environment, so a promoted A/B state is expressed entirely in the config file. Verified on the gfx1100 rollout: the promoted resident serves with 'prefill gemm: hipBLASLt tuning enabled (30 rows, gfx1100, version 100100)' read from the config-driven env; paired A/B (results/tune-20260929T175811Z, T184449Z): long-prompt prefill 416->971 tok/s, decode -0.8% (inside the 3% protected cap), winner-vs-original decode +38.3%.
# Conflicts: # CMakeLists.txt # README.md # docs/AMD_HIP.md # docs/AMD_HIP_PERFORMANCE.md # include/strata/hip_compat/cuda_runtime.h # src/core/device.cu # src/kernels/cuda/qsa_prompt_attn.cu # src/kernels/cuda/qsa_select.cu # src/prefill/prefill.cpp # tests/hip/prefill_native_batch.cpp # tests/platform/direct_file_async.cpp
Re-application of d7a0d6b after the 0.1.27 merge (the upstream tree still has both defects): qsa_prompt_attn_parity links CUDA::cudart unconditionally, and native_mmvq_multi names bench/micro/native_mmvq_multi.cpp, a source in no commit of this repository or upstream. Verified by a HIP-only configure with STRATA_BUILD_TESTS=ON and the CTest run.
telemetry.py reads the GPU through NVML, so on an AMD box (no libnvidia-ml) every GPU series is absent: the Monitor's GPU load, VRAM, temperature, power and PCIe cards show "-", and hardware_static.gpu_name is null. Where psutil is not installed, the disk rate shows "needs psutil (setup installs it)" and the physical-core count is missing. _AmdSysfs reads the same fields from amdgpu's sysfs: the card is found by scanning /sys/class/drm/card*/device for DRIVER=amdgpu (the number is not fixed - a box with another DRM device can have the AMD card at card1), the temperature is the hwmon sensor labelled "edge" (what NVML calls the GPU temperature), power comes from power1_average/power1_cap, and the link from current/max_link_speed plus current_link_width. Every reading is a small file read, so a sample cannot block the server, and anything the kernel does not expose stays None rather than being invented. amdgpu has no PCIe throughput counter, so the link is reported and the MB/s series stays empty; the web app already renders "Gen4 x16" from those fields. The patch also adds /proc/diskstats (the source psutil itself reads; whole disks only, never partitions and never loop/zram/dm, which would count a device-mapper layer twice) and /proc/cpuinfo's (physical id, core id) pairs for the psutil-less case. NVML still wins whenever it loads, and the psutil path is untouched, so an NVIDIA install behaves exactly as before. Tests: serve/test_telemetry.py needs no GPU - the pure helpers, the no-card case, and a sample that must still carry every series the Monitor plots. (Rebased onto the multi-card Monitor f7262d0: the AMD backend now feeds the per-card readings and their total/mean/hottest aggregation of issue Niko1221#112 too. NVML still wins when it loads - the follow-up commit changes that for AMD.)
Two rules for boxes where the amdgpu scan or NVML alone picked the wrong card: - The _AmdSysfs card is the one at the model device's PCI slot (HIP ordinal -> PCI slot via libamdhip64, matched to the DRM card's uevent), not 'the index-th amdgpu' - a Ryzen iGPU is amdgpu too and takes index 0 of the card scan. - When the model runs on the AMD card the readings come from amdgpu's sysfs even where NVML loads (it ships with the NVIDIA driver and used to win), because 0.1.26's setup lets a mixed PC choose AMD. 'Runs on AMD' is the config's backend key (setup's own signal) or, without one, the engine binary's own linkage to libamdhip64 (a string-table read; the binary is not executed). On NVIDIA-only boxes nothing changes: NVML still wins there. Tests: serve/test_telemetry.py 8/8 with a fake sysfs (iGPU vs dGPU selection), fake backends (AMD wins over a loading NVML exactly when the model is on AMD) and the backend decision cases. docs/AMD_HIP.md no longer says the Monitor has no GPU statistics.
The fork's clock64() stamp variant conflicted with upstream's wall_clock64() rework at the merge (the splice did not compile). The wall-clock form is kept: it reports true nanoseconds, which is what the verify-window profiler's span labels assume; the clock64 form's 'relative spans only' caveat is a downgrade. This is the exact tree the 0.1.27 gates and the version A/B ran on.
…'s queries (bit-identical; STRATA_SCORES_MULTI=0 = old grid)
The CUDA TF32 scorer cannot run on gfx1100 (no mma.sync). This adds qsa_block_scores_wmma: a 16x16x16 ROCWMMA GEMM view of the block scores (4 queries x 4 indexer heads per row tile, 128 dims in 8 K-slices, FP16 operands with FP32 accumulation), with the tail block n_bid kept bitwise the warp kernel's by reusing block_scores_tail_kernel unchanged. Keys and queries are converted to FP16 per call into an internal scratch (poisoned padding, PORTING.md 18); the arithmetic contract and the score bound B(qi,b) = 2^-9.5*S + 8*2^-24*|score| are recorded in PORTING.md 18. Prompt-path dispatch is opt-in (STRATA_SELECT_WMMa=1) and prints the arm that actually ran, so a fallback cannot masquerade. Builds compile the arm out (and the entry refuses) without rocWMMA headers; a CUDA build is untouched. Also based on PR Niko1221#187 (the multi-query scorer).
qsa_select_wmma_parity runs four cases - ragged query and block tiles, whole tiles, x8-magnitude headroom near FP16 overflow, and a degenerate n_kv=0 step - against a float64 oracle under the bound derived in PORTING.md 18 before the kernel existed, and against the warp kernel's exact host model. The harness pins the device's FMA contraction pattern (fma-left: 100% bitwise fit, plain and fma-right fit only the uncontracted subset) instead of moving any tolerance. Results: worst bound utilisation 9.5-11.0% on every case; tail block bitwise the warp arm's; write set identical (canary intact); selection IDs identical; the negative control (+2.5*B perturbation) fails the checker as it must. Also: qsa_select_wmma_parity CMake target (HIP + rocWMMA, with the tests).
qsa_select_bench existed unbuilt upstream; give it a target and a third arm (qsa_block_scores_wmma, staging + main + tail as the prompt path calls it) with the tc arm skipped gracefully where it refuses. Timing becomes a median of 9 samples of reps calls after 3 warmup samples. Bench (RX 7900 XTX, nq=256, 256-32769 blocks): scores warp 0.041/0.072/0.655/ 1.398/2.627 ms vs wmma 0.028/0.023/0.066/0.107/0.252 ms at 1K/4K/32K/64K/128K (1.5x/3.2x/9.9x/13.0x/10.5x); selection cells differing 0-0.009%.
… hc-read second half stops overwriting the next layer's base stamp kProfPer 32 -> 33: fused_gr_read_multi writes three stamps per half and the second half's base was 30, so its up-stamp landed at slot 32 = (l+1)*32 + 0, the next layer's stage-0 base. Derived columns (gap/head/hc0) also wrapped to ~1.8e19 ns whenever a slot was unstamped, printing ~1e14 ms per window. A delta now counts only when both stamps are nonzero and monotonic; the hc0 split replaces the single hc-read0 column only when the split chain is complete. Metrics-only: no computed model value changes. Reason recorded in PORTING.md 19.
…-select-upstream # Conflicts: # src/core/verify.cpp
…-select-upstream # Conflicts: # CMakeLists.txt # src/kernels/cuda/qsa_select.cu
…ded rows) and --selftest The pattern-1 rows make every expected dot product 0 or exactly representable, so a lane duplication, transpose or store error cannot hide inside a tolerance. This is the version the 5/5 parity record was produced with (record-qsa-select-wmma-parity-20260930.json) and that patch 0011-qsa-select-wmma.patch carries into the rollout tree.
xyzzing
marked this pull request as ready for review
September 30, 2026 13:55
Owner
|
Thanks! We'd take the rocWMMA scorer as an opt-in ( |
This was referenced Sep 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three independent parts; each is separately reviewable. Built on PR #187's multi-query
block scorer (cherry-picked as
ed70478in this branch — if you'd rather merge #187first, the branch rebases onto it cleanly;
git merge-treeshows no other overlap).1.
qsa_block_scores_wmma: the prompt scorer arm on tensor coresThe prompt path's QSA block scores (
score(qi,b) = sum_h relu(sum_i k_b[i] · q_qi,h[i]))now have a rocWMMA 16×16×16 FP16→FP32 implementation beside the warp kernel. gfx11 has no
mma.sync, and this is the tensor-core path that actually runs there — verified at theinstruction level: the gfx1100 code object executes 8
v_wmma_f32_16x16x16_f16(the 8K-slices of the mma loop) with
block_scores_tail_kernelandf32_to_f16_poison_kernelalongside.
Opt-in, with an honest fallback.
STRATA_SELECT_WMMA=1tries the arm first; the hostentry refuses (wrong geometry, not gfx1100, no rocWMMA) and the warp kernel keeps the
work. The arm that actually ran prints once (
strata select: prompt scorer arm = ...) soa fallback cannot masquerade as the fast path. No other device changes behavior;
every non-gfx1100 device keeps the warp kernel by construction.
The arithmetic contract was derived before the kernel (PORTING.md §18 in our rollout
repo, excerpt below): FP16 operands and an unspecified accumulation order put every
score within
of the float64 reference — and the tail block is bitwise the warp kernel's (the shared
tail kernel runs unchanged). No tolerance was moved at any point.
Parity suite (
src/kernels/qsa_select_wmma_parity.cpp, in the test suite): tworeferences (a float64 oracle and a warp-order host model pinned to the observed FMA
contraction), five cases — ragged query/block tiles, whole tiles, ×8 magnitude headroom
(near FP16 overflow), degenerate
n_kv=0, and one-hot/coordinate-coded adversarialfixtures that expose lane-duplication/transpose/store errors at zero tolerance. Plus: the
write set is exactly the warp kernel's (canary-asserted), selection IDs agree, and a
negative control (+2.5·B perturbation) fails the same checker. Measured bound
utilisation 0.0–11.0% worst case.
Bench (
qsa_select_bench, nq=256, median of 9 after 3 warmups) — scores only:Selection cells differing vs the warp arm: 0–0.009%.
To keep the numbers honest at both levels: the bench table is the scores kernel; the
whole
qsa selectphase (scores + selection/topk + the fp16 conversion + the batch loop)speeds up less — at 128K it drops 13,592 ms → 5,945 ms (~2.3×) on the measured e2e runs.
The kernel speedup is the arm; the phase speedup is what the prompt actually sees; the
end-to-end row below is what the request sees.
End-to-end, paired (frozen contract: 5 interleaved pairs × 2 slots per length,
order alternating per pair, the tuned AMD engine, prompt GPU timeline):
qsa select1 ms -> 1 ms - flat: the phase is a sliver of the timeline)Promotion gate: >=5% at 128K on the prompt GPU timeline, decode protected to 3%.
All three gates PASS -> PROMOTE (default flip earns its gates) on the complete contract
(10 pairs per length, exclusions declared in the record):
shared code between the arms, so the expectation is zero regression; the clean pairs read
wmma +2-3% faster, the interference-dipped pairs scatter both ways. The decision statistic is
the paired per-cell ratio median - the campaign's pre-declared drift-robust estimator, the one
gates 1 and 3 already use. For full transparency the record also carries the ratio-of-arm-medians
(0.937): on this bimodal decode distribution (5 clean / 5 interference-dipped cells) the arm
medians land in the distribution gap and flip with sampling order, so that variant is reported
but is not the decision statistic. The <=3% threshold itself never moved.
2. The AMD RX 7900 XTX (gfx1100) IQ3_S row in the rates tables
Prompt (tokens/s): collecting (the 1K-128K sweep record; 262K "-")
Output (tokens/s): collecting (the same runs)
(262K: "-", per the IQ3_S row's convention.)
Methodology follows
bench/results/2026-09-29-speed-0126: one-shotstrata generateruns, greedy, 256 generated tokens, the tuned AMD engine config. Every cell is a row in
artifacts/rocm/results/amd-rates-20260930.jsonwith its raw engine log kept.3.
verify: the profiler's stage columns stop lying (PORTING.md §19)Two integer-arithmetic defects made
STRATA_VERIFY_PROFILE's report print ~1e14ms/window for the "(gap)"/"head"/hc0 columns:
kProfPer = 32left the hc-read second half's up-stamp at slot 32 — the next layer'sstage-0 base stamp — corrupting the next layer's first delta.
kProfPeris now 33 and a delta counts only when both stamps are nonzero and monotonic.Metrics-only: no computed model value changes. The GPU-side attribution of the residual
(the no-doorbell verify-window floor for native packs) is recorded in our rollout repo.
What this is not
not promoted).
measured). Wider RDNA3/4 support is a per-SKU measurement, not a flag flip.
Evidence lives in the xyzzing/strata-amd rollout repo: PORTING.md 18/18.1/19,
record-qsa-select-wmma-parity-*.json,record-qsa-select-e2e-*.json,record-prepr-merge-audit-*.json,record-d8-stamp-fix-*.json,ab-qsa-select-20260930.json, and the D8 capture (baseline vs no-doorbell,STRATA_VERIFY_DEVICE_PLAN=1) that attributes the ~26 ms/window residual.Engine attribution (after the v0.1.29 merge)
The branch now includes
upstream/mainat v0.1.29 (merge0975f72); upstream's reviewedimplementation of the multi-query scorer (#187) supersedes the cherry-pick this PR was
originally stacked on. The two merge conflicts were comment-only in
qsa_select.cu(upstream's comments kept) and one informative comment in
CMakeLists.txt(ours kept);the merged tree builds 236/236.
The measurements above were produced by the pre-merge, engine-0.1.28-based build (the
campaign was mid-run on it); each record names its binary and engine. GPU parity
re-validation of the merged build is scheduled at the campaign's end and will be posted
here before this draft is marked ready.