Fix DecoupledLookback cross-block coherence (completes #91) - #98
Fix DecoupledLookback cross-block coherence (completes #91)#98shreyas-omkar wants to merge 4 commits into
Conversation
…kback Re-enable DecoupledLookback() coverage in the non-uniform exclusive accumulate test (excluded on the main branch). This branch carries the work-in-progress fix for DecoupledLookback's exclusive carries, so it must exercise that path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…need The decoupled-lookback aggregate publish/consume protocol is not memory- coherent across blocks (stale L1 aggregate reads + missing device-scope ordering), causing ~40% failures on smaller GPUs. The proper fix needs a native device threadfence; `UnsafeAtomics.fence` and acquire/release atomics do not lower on recent NVPTX toolchains (LLVM 18, sm_80) — only `monotonic` does. Document this inline as the path forward. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
9ea5555 to
63a1fcc
Compare
cdc6568 to
35e1b04
Compare
…D/oneAPI Completes JuliaGPU#91. Three parts, all in accumulate_1d_gpu.jl: - Coherence: publish/consume the cross-block aggregate with an L1-bypassing relaxed (monotonic) atomic through a dedicated `aggregates` array, ordered by a device-scope fence (`_decoupled_fence`; CUDA extension narrows it to `threadfence`). A consumer that observes a block's flag can no longer read a stale aggregate — fixes the ~40%-on-small-GPUs race. - AMDGPU gfx1200 (RDNA4): declare `_accumulate_block!` bounds-checked and re-apply `@inbounds` only to the hot, provably-in-bounds accesses. Forcing `@inbounds` over the whole body made ROCm miscompile the multi-block path into an illegal address (a codegen bug — correct under `--check-bounds=yes`), faulting both ScanPrefixes and DecoupledLookback for n > 2*block_size. - oneAPI / SPIR-V: relaxed-atomic pointers preserve their address space (`reinterpret(Core.LLVMPtr{U,AS}, ...)` instead of dropping to generic `Ptr`), and the lookback `flags` are UInt32 not UInt8 (SPIR-V/Level Zero has no 8-bit atomics). DecoupledLookback now compiles and runs on Intel. Validated: CUDA (RTX 5080), AMDGPU (RX 9060 XT / gfx1200), oneAPI (Iris Xe) — ScanPrefixes and DecoupledLookback correct across sizes and inclusive/exclusive; accumulate and reduce test suites green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
35e1b04 to
22c5c93
Compare
1. Cross-block coherence (completes #91)Each block publishes its inclusive aggregate through a dedicated 2. AMDGPU gfx1200 (RDNA4) illegal-address
3. oneAPI / SPIR-V compile failureDL didn't compile on Level Zero. Two SPIR-V limitations: (a) relaxed-atomic pointers were Validation
|
|
I don't know that an extension in this repository is the best place for a device-scope fence workaround. I'm also not sure what a proper fix should look like. @vchuravy do you have any suggestions on a better approach? |
Completes #91 — a working version of the
DecoupledLookback()scan.Background
#91 corrected
DecoupledLookback()'s exclusive multi-block carries by publishing each block's inclusive aggregate in a dedicated array. That fixed the algorithm, but the cross-block publish/consume protocol was still not memory-coherent, so it failed ~40% of the time on smaller GPUs (e.g. A100 MIG 1g.5gb, sm_80) for both inclusive and exclusive scans.Two problems remained:
flag == ACC_FLAG_Ayet read a stale aggregate.Fix
monotonic), L1-bypassing atomic, reinterpreted through a same-width unsigned integer so float element types are supported (compile-time fallback to a plain access for other widths).There is no vendor-agnostic device-scope fence in the ecosystem today (KA only has work-group
barrier/@synchronize;UnsafeAtomics.fence(acq_rel)does not lower on NVPTX;CUDA.threadfenceis CUDA-only). So_decoupled_fence()composes the already-available primitives, kept self-contained here:UnsafeAtomics.fence(acq_rel)(lowers on OpenCL/SPIR-V, Metal, oneAPI, AMDGPU),CUDA.threadfence()(membar.gl) via a small CUDA package extension, since NVPTX does not select scoped atomic fences.No new hard dependency:
CUDAis a weakdep behind an extension.Also:
aggregatesis now always its own contiguous array (never a temp view) sopointer(aggregates, i)is valid inside the kernel; the non-uniform exclusive-scan test is re-extended to coverDecoupledLookback().Validation
Performance / when to use it
DecoupledLookback()is now correct, but it is not the fastest scan on CUDA — launches are cheap there, so the standaloneScanPrefixes()(which stays the default on every backend) wins, and the gap grows withn:So this PR does not change the default (
ScanPrefixes()everywhere). Its value is makingDecoupledLookback()correct so it can be opted into on dispatch-bound backends (e.g. Metal), where collapsing kernel launches is the win — the motivation for the single-pass/onesweep direction.