Skip to content

HRX: decode-split multipass path intermittently faults (HSA_STATUS_ERROR_MEMORY_FAULT) above capacity 2048 #123

Description

@1bit-traffic-bot

The multi-pass KV-block reduction added by 1bit-MONSTER/llama.cpp#13 (branch feat/hrx-decode-split-multipass, commit f59f6d37a on fa1a4563) makes flash_attention.decode_split work above key_value_token_capacity 2048, but a residual out-of-bounds access intermittently faults the decode instead of declining. That violates the HRX lane's constraint that a dispatch must decline and fall back rather than fail the decode.

Symptom

gfx1151 (Strix Halo), Qwen3-Coder-30B-A3B-Instruct-Q4_K_M, -dev HRX0, llama-bench -p 0 -n 8 -d <D>:

Warning: Queue error - HSA_STATUS_ERROR_MEMORY_FAULT
E graph_compute: ... AMDGPU memory access fault at device address 0x00007f508ea94000 (reason mask 0x00000001)
E llama_decode: failed to decode, ret = -3

5-run GPU-fault counts with the partial transients aligned to 4096:

depth capacity blocks faults
1900 1920 30 0/5
2000 2048 32 0/5
2100 2112 33 0/5
2500 2560 40 0/5
3000 3008 47 1/5
4800 4864 76 0/5

With the SAME code but the partial transients aligned to 256, d2100 is 5/5. GGML_HRX_DISABLE_DISPATCH=flash_attention_decode_split makes every faulting depth pass (0/5), so the fault is in the multipass path, not the fallback.

Excluded (each measured over 5 runs)

  • The reducer - no-op'ing reduce_completed.multipass (inserting template.return as its first statement) still faults 5/5, so it is not the reduction math.
  • The completion counter - replacing its atomic.reduce<addi> -N reset with a plain release-ordered store of 0 changes nothing; so does zero-initialising it via ConstantInitialization.
  • The exact-33 assume bound - relaxing every range(..., 33, 4096) to range(..., 1, 4096) changes nothing.
  • Static sizing - the dispatch's transient bytes are exactly 256 * blocks (partial_max/sum) and 16384 * blocks (partial_output), strictly linear, and the kernel's views match.
  • Every readable access is in bounds - the produce's channel stores (lane_output_base = lane * 4, lane < 32, so the channel is in {0,4,...,124} and a vector<4xf16> store keeps channel + 4 <= value_head_size = 128); the scalar max/sum row stores (guarded by query_head_valid); the reduce's output store (query_head = key_value_head * query_heads_per_key_value_head + query_row < 32, output_channel < value_head_size); the K/V loads (element-guarded by bounded_key_value_token_count); pack_completed_q8's output size (q8_1_x4_byte_count(1, output_hidden_size)).
  • Allocation - alignment 256 -> 4096 helps (d2100 5/5 -> 0/5); padding the transient SIZES makes it worse (d3000 5/5, d2100 4/5); 64K alignment is worse still; padding the block count to whole 64-block waves does not help.

Assessment

Every access is provably in bounds, yet the fault moves with the allocation layout and cannot be removed by adding slack. The best remaining explanation is a wrong address emitted by the JIT-specialised code for this capacity, which allocation cannot fix. Prime suspect: the multipass output pass (flash_attention_decode_split_f32_f16_wmma.loom ~911-920), the only O(blocks)-per-output-element code in the reduce.

Impact

1bit-MONSTER/llama.cpp#13 masks part of this by aligning the partial transients to 4096, but the fault is not eliminated and a decode can still fail - so this is not yet safe to land as-is.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions