Skip to content

rocm: vectorized HIP PTQ1_0 vec_dot - #211

Merged
bri-prism merged 3 commits into
PrismML-Eng:prismfrom
Ankk98:strix-halo-optimization
Sep 23, 2026
Merged

bri-prism merged 3 commits into
PrismML-Eng:prismfrom
Ankk98:strix-halo-optimization

Conversation

@Ankk98

@Ankk98 Ankk98 commented Sep 19, 2026 •

Copy link
Copy Markdown

Overview

Through this branch I am trying to optimize Bonsai 2 27B to run efficiently on strix-halo through rocm path.

The model file stores weights as densely packed base-3 digits (trits). To use them, the GPU must unpack each trit and multiply-accumulate it with the activation. The old AMD code did this one trit at a time in a long serial chain (128 slow steps per block). The new code unpacks 4 trits at once using the GPU's byte-shuffle and 4-wide dot-product instructions. Same math, ~3x less instruction work, which is why decode got ~88% faster.

A wrong guess about a shuffle instruction's argument order was caught by on-device probes and unit tests before it could do harm; the corrected convention is now documented in a code comment.

Additional information

  • Tested L2 13593/13593
  • Deterministic end to end outout verified as compared to parent branch
  • Tested cross-arch compile (gfx90a/1030/1100/1151)

Bench summary (AC + high-perf, -ngl 99 -fa on -ub 2048, cooled between runs)

test before (scalar) after (vec)
tg128 11.47 21.0 - 21.5 (+~88%)
tg32 9.29 20.3 - 21.3 (+~120%)
tg128 ub512 8.89 19.90 (+124%)
pp512 175.4 186 - 204 (+6-16%)

Test Hardware

  • OS: Fedora Linux 44 (Workstation Edition) x86_64
  • Host: ROG Flow Z13 GZ302EA_GZ302EA (1.0)
  • Kernel: Linux 7.2.5-200.fc44.x86_64
  • CPU: AMD RYZEN AI MAX 390 (24) @ 5.06 GHz
  • GPU: AMD Radeon 8050S Graphics [Integrated]

Notes

  • Taken assistance of Opencode & Muse 1.3

Requirements

The model file stores weights as densely packed base-3 digits (trits). To use
them, the GPU must unpack each trit and multiply-accumulate it with the
activation. The old AMD code did this one trit at a time in a long serial
chain (128 slow steps per block). The new code unpacks 4 trits at once using
the GPU's byte-shuffle and 4-wide dot-product instructions. Same math, ~3x
less instruction work, which is why decode got ~88% faster. A wrong guess
about a shuffle instruction's argument order was caught by on-device probes
and unit tests before it could do harm; the corrected convention is now
documented in a code comment.
@Ankk98 Ankk98 changed the title rocm: vectorized HIP PTQ1_0 vec_dot [WIP] rocm: vectorized HIP PTQ1_0 vec_dot Sep 19, 2026

@bri-prism bri-prism left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent review: posted by the maintainer's coding agent at their request.

No findings in this source pass. The four-byte trit extraction, element mapping and integer activation-sum correction are consistent with the previous scalar HIP path.

HIP compilation and device numerical tests were not run here. Before merging, retain a comparison against the scalar path that covers all packed-byte values and the qh tail on the supported AMD targets.

Reviewed commit: e435472d5391720bda720fbbbebf8d75938f8fca.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The architecture-specific vector path lacks regression coverage for its byte-permute and lane-packing behavior.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 Medium severity · 1 Low severity

Open (2)

Comment on lines +926 to +928
const int q = (int) __builtin_amdgcn_perm(w_hi, w_lo, 0x07050301);
sumi[e >> 5] += ggml_cuda_dp4a(q, u, 0);
sumu[e >> 5] += ggml_cuda_dp4a(0x01010101, u, 0);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed the comment, extended tests/test-ptq1_0-cuda-dot.cpp with:

  • a host transcription of the HIP vector idiom, unsigned {0,1,2} digits, bias folded into activation sums, qh tail in the unsigned scheme;
  • a hip_perm emulator encoding the device-proven rule, including the non-obvious part I verified while writing it: selectors are byte-granular ((s >> 8*i) & 7), not nibbles, nibble indexing contradicts the [b0,0,b1,0] comments and fails everything, byte indexing passes everything;
  • per-trial vec-vs-reference integer checks plus an exhaustive sweep (all 256 byte values × 6 packed words + 2 qh bytes × 4 chunks = 8192 exact checks).

Results: 20000 exact integer checks, 0 mismatches; 8192 exhaustive, 0 mismatches; exit 0. Device side stays covered by test-backend-ops on gfx1151.

Comment thread ggml/src/ggml-cuda/vecdotq.cuh Outdated
Comment on lines +904 to +909
// Four packed bytes advance in the low bytes of 16-bit lanes, so one
// 32-bit multiply steps four trit streams at once (3*255 < 2^16, no
// cross-lane carry). Digits come out as {0,1,2}; the -1 folds into the
// activation sums, which a per-byte subtract cannot do borrow-free.
// NB: __builtin_amdgcn_perm picks sel bit2=0 from the SECOND arg, the
// opposite of CUDA __byte_perm, so the operands are swapped vs _multi.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Transcribe the vector idiom (unsigned digits, bias fold, swapped-operand
perm, qh tail) and assert it against the scalar reference over all 256
byte values at every word position. Unwrap the vecdotq comment to one
sentence per line.

Assisted-by: Opencode with Muse Spark

@bri-prism bri-prism left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at 85a6c5f. The vectorization is correct on my read, the test is a real gate, and there is one evidentiary gap plus a sequencing hazard with #215.

The arithmetic checks out. I traced it by hand rather than trusting the commit message. Folding the bias as sum(q*a) - sum(a) with unsigned {0,1,2} digits is algebraically equivalent to the signed form. The element mapping is preserved: the scalar path's e = t*16 + m with m = 4w + j gives the same index the vector path computes as e = t*16 + 4w + j. The 0x07050301 selector places digit(b0..b3) in result bytes 0..3, which matches the little-endian order get_int_b4 loads the activations in. All three loops together touch each of the 128 elements exactly once, 80 plus 40 plus 8. No overflow is possible: sumu peaks near 32127 and sumi near 232*127, both far inside int32. The (e & 31) >> 2 group index stays 4-element aligned in both main loops. No findings there.

The byte-granular selector discovery is the valuable part of this PR and I am glad it is written down. Selectors being (s >> 8*i) & 7 rather than nibble-indexed, with sel bit2 = 0 choosing the second operand, is the kind of thing that costs someone a day.

Where I think the evidence is thinner than it looks.

hip_perm in the test is a host emulation of __builtin_amdgcn_perm, and vec_dot_ptq1_0_q8_1_vec is a transcription of the kernel rather than the kernel. That means the suite establishes that the algorithm is self-consistent given a belief about the intrinsic. It cannot establish the belief.

This matters more than usual here because of how the emulation was arrived at. You describe trying nibble indexing, finding it fails everything, then adopting byte indexing because it passes everything. That is fitting the emulator until the algorithm agrees with the reference. It is a reasonable way to find the rule, but it means the emulator and the kernel now share whatever assumption makes them agree, and the host test can no longer discriminate between "this matches the hardware" and "these two copies match each other". If the real intrinsic differs from hip_perm in some case neither of us has thought of, every one of those 28192 checks still passes and the device still returns wrong numbers.

The one artifact that would settle it is the device run, and that is the one reported without numbers. The host results are given precisely, 20000 exact integer checks and 8192 exhaustive, 0 mismatches, exit 0. The device side is given as "stays covered by test-backend-ops on gfx1151", which is an assertion rather than a result. Could you post the actual test-backend-ops output for the PTQ1_0 cases at 85a6c5f, with the case count? My earlier note asked for validation on the supported AMD targets, and gfx1151 output would close it outright.

Two smaller things.

The exhaustive sweep sets all four bytes of a packed word to the same value via v * 0x01010101u, so a within-word byte transposition such as b0 against b1 would pass that loop unnoticed. The 5000 random trials do use independent bytes and compare against ref_dequant, so the case is covered overall and I am not asking for a change. It is only that the exhaustive loop alone proves less than its name suggests.

The test carries its own copy of the kernel body. A later edit to the HIP branch in vecdotq.cuh will not be caught unless someone remembers to edit the transcription too. A short comment at the top of the HIP branch pointing at tests/test-ptq1_0-cuda-dot.cpp would make that link visible to whoever touches it next.

Sequencing with #215. Both PRs modify vec_dot_ptq1_0_q8_1, on opposite branches of the same #if. This PR leaves the #else calling vec_dot_ptq1_0_q8_1_multi, which is correct. #215 replaces that same #else with return 0.0f, which I have flagged there as a regression that silently zeroes PTQ1_0 on MUSA. Neither branch contains the other, and the two versions of this one function differ by about fifty lines, so they will conflict. Whoever resolves that conflict could reinstate the MUSA stub without noticing it is a defect, since it looks like an intentional part of the other change. Worth agreeing an order before either lands.

Method note: source reading and hand-tracing at 85a6c5f. I did not build, and I have no AMD hardware, so nothing above is a device result.

@Ankk98

Ankk98 commented Sep 22, 2026

Copy link
Copy Markdown
Author

Device code is byte-identical between e435472 and 85a6c5f (the delta is comment + host test only). So, this run covers both.

  • Full suite 13593/13593 green on the same device code
  • Host side 20000 exact integer checks, 8192 exhaustive, exit 0
  • Pointer comment linking the HIP branch to the test added in 3a82de9
  • On the transposition blind spot: acknowledged, no change — the 5000 random trials use independent bytes per position and check against ref_dequant, so that case is covered overall, as you note.

test-backend-ops test -b ROCm0 -j 16 -p 'type_a=ptq1_0' → 120/120 pass (skipped not supported combos excluded from the count):

ggml_cuda_init: found 1 ROCm devices (Total VRAM: 24576 MiB):
  Device 0: AMD Radeon 8050S Graphics, gfx1151 (0x1151), VMM: no, Wave Size: 32, VRAM: 24576 MiB
Testing 2 devices

Backend 1/2: ROCm0
  Device description: AMD Radeon 8050S Graphics
  Device memory: 24576 MB (24575 MB free)

  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=8,k=256,bs=[2,3],nr=[1,1],per=[0,1,3,2],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=1,b=1,m=512,n=5,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=1,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=1,k=128,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=4,b=0,m=512,n=1,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=4,b=1,m=512,n=5,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=1,k=256,bs=[3,2],nr=[2,2],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=2,b=1,m=512,n=5,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=4,b=1,m=512,n=4,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=2,b=0,m=512,n=17,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=2,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=1,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=4,k=256,bs=[3,2],nr=[2,2],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=1,b=1,m=512,n=17,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=4,b=1,m=512,n=17,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=4,b=1,m=512,n=5,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=1,b=0,m=512,n=129,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=2,b=1,m=512,n=17,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=4,b=0,m=512,n=4,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=8,k=256,bs=[2,3],nr=[1,1],per=[0,3,2,1],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=2,b=0,m=512,n=32,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=1,n=64,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=16,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=3,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=1,b=1,m=512,n=32,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=2,b=1,m=512,n=32,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=1,b=1,m=512,n=1,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=4,b=1,m=512,n=32,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=16,k=256,bs=[2,3],nr=[1,1],per=[0,2,1,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=4,b=0,m=512,n=5,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=4,b=1,m=512,n=17,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=16,k=256,bs=[1,1],nr=[2,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=2,b=0,m=512,n=129,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=2,b=0,m=64,n=16,k=384): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=4,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=1,b=1,m=512,n=129,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=16,k=256,bs=[1,1],nr=[1,2],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=1,b=0,m=512,n=1,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=1,b=1,m=512,n=4,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=2,b=1,m=512,n=1,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=4,b=0,m=512,n=17,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=2,b=1,m=512,n=129,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=4,b=1,m=512,n=32,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=5,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): ggml_backend_cuda_graph_compute: CUDA graph warmup complete
ggml_backend_cuda_graph_compute: CUDA graph warmup complete
ggml_backend_cuda_graph_compute: CUDA graph warmup complete
ggml_backend_cuda_graph_compute: CUDA graph warmup complete
OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=16,k=256,bs=[2,3],nr=[1,1],per=[0,1,3,2],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=4,b=1,m=512,n=129,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=2,b=0,m=512,n=1,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=16,k=256,bs=[3,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=6,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=2,b=1,m=512,n=4,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=1,b=0,m=512,n=4,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=1,b=1,m=512,n=5,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=4,b=0,m=512,n=1,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=4,b=0,m=512,n=32,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=2,b=0,m=512,n=1,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=4,b=1,m=512,n=129,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=16,k=256,bs=[2,3],nr=[1,1],per=[0,3,2,1],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=2,b=0,m=512,n=4,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=16,k=256,bs=[3,1],nr=[2,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=7,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=2,b=1,m=512,n=5,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=1,b=0,m=512,n=5,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=4,b=0,m=512,n=4,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=1,b=1,m=512,n=17,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=1,k=1024,bs=[3,2],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=2,b=0,m=512,n=32,k=256): OK
  OUT_PROD(type_a=ptq1_0,type_b=f32,m=256,n=1,k=1,bs=[1,1],nr=[1,1],trans_b=0): not supported [ROCm0] 
  OUT_PROD(type_a=ptq1_0,type_b=f32,m=256,n=1,k=1,bs=[1,1],nr=[1,2],trans_b=0): not supported [ROCm0, CPU] 
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=4,b=0,m=512,n=129,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=1,b=0,m=512,n=1,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=16,k=256,bs=[3,2],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=8,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=2,b=0,m=512,n=5,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=2,b=1,m=512,n=17,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=1,b=0,m=512,n=17,k=256): ggml_backend_cuda_graph_compute: CUDA graph warmup reset
ggml_backend_cuda_graph_compute: CUDA graph warmup reset
ggml_backend_cuda_graph_compute: CUDA graph warmup reset
OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=8,k=1024,bs=[3,2],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=1,b=1,m=512,n=32,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=4,b=0,m=512,n=5,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=4,b=1,m=512,n=1,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=1,b=0,m=512,n=4,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=16,k=256,bs=[3,2],nr=[2,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=9,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=2,b=0,m=512,n=17,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=2,b=1,m=512,n=32,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=16,k=1024,bs=[3,2],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=1,b=0,m=512,n=32,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=4,b=0,m=512,n=17,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=4,b=1,m=512,n=4,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=1,b=1,m=512,n=129,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=1,b=0,m=512,n=5,k=256): ggml_backend_cuda_graph_compute: CUDA graph warmup complete
ggml_backend_cuda_graph_compute: CUDA graph warmup complete
ggml_backend_cuda_graph_compute: CUDA graph warmup complete
ggml_backend_cuda_graph_compute: CUDA graph warmup complete
OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=16,k=256,bs=[3,2],nr=[1,2],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=1,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=2,b=0,m=512,n=32,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=2,b=1,m=512,n=129,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=4,b=0,m=512,n=32,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=1,b=0,m=512,n=129,k=256): ggml_backend_cuda_graph_compute: CUDA graph warmup reset
ggml_backend_cuda_graph_compute: CUDA graph warmup reset
ggml_backend_cuda_graph_compute: CUDA graph warmup reset
ggml_backend_cuda_graph_compute: CUDA graph warmup reset
OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=16,k=256,bs=[3,2],nr=[2,2],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=2,b=0,m=512,n=1,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=1,k=256,bs=[1,1],nr=[2,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=1,b=0,m=512,n=17,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=8,k=256,bs=[1536,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT(type_a=ptq1_0,type_b=f16,m=16,n=1,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): not supported [ROCm0, CPU] 
  MUL_MAT(type_a=ptq1_0,type_b=f16,m=16,n=1,k=256,bs=[1,1],nr=[2,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): not supported [ROCm0, CPU] 
  MUL_MAT(type_a=ptq1_0,type_b=f16,m=16,n=1,k=256,bs=[1,1],nr=[1,2],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): not supported [ROCm0, CPU] 
  MUL_MAT(type_a=ptq1_0,type_b=f16,m=16,n=1,k=256,bs=[3,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): not supported [ROCm0, CPU] 
  MUL_MAT(type_a=ptq1_0,type_b=f16,m=16,n=1,k=256,bs=[3,1],nr=[2,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): not supported [ROCm0, CPU] 
  MUL_MAT(type_a=ptq1_0,type_b=f16,m=16,n=1,k=256,bs=[3,2],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): not supported [ROCm0, CPU] 
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=2,b=0,m=512,n=129,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=1,b=1,m=512,n=1,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=1,k=256,bs=[1,1],nr=[1,2],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=1,k=256,bs=[2,3],nr=[1,1],per=[0,2,1,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=2,b=0,m=512,n=4,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=4,b=0,m=512,n=129,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=1,b=0,m=512,n=32,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=1,b=1,m=512,n=4,k=256): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=2,b=1,m=512,n=1,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=1,k=256,bs=[3,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=4,b=1,m=512,n=1,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=1,k=256,bs=[2,3],nr=[1,1],per=[0,1,3,2],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=8,n_used=2,b=0,m=512,n=5,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=1,k=256,bs=[3,1],nr=[2,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT_ID(type_a=ptq1_0,type_b=f32,n_mats=4,n_used=2,b=1,m=512,n=4,k=256): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=1,k=256,bs=[2,3],nr=[1,1],per=[0,3,2,1],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=1,k=256,bs=[3,2],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=1,k=256,bs=[3,2],nr=[2,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=4,k=256,bs=[2,3],nr=[1,1],per=[0,3,2,1],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=1,k=256,bs=[3,2],nr=[1,2],per=[0,1,2,3],k_v=0,o=1,src_overlap=0): OK
  MUL_MAT(type_a=ptq1_0,type_b=f32,m=16,n=8,k=256,bs=[2,3],nr=[1,1],per=[0,2,1,3],k_v=0,o=1,src_overlap=0): OK
  120/120 tests passed
  Backend ROCm0: OK
Backend 2/2: CPU
  Skipping
2/2 backends passed
OK

Sequencing with #215. Both PRs modify vec_dot_ptq1_0_q8_1, on opposite branches of the same #if. This PR leaves the #else calling vec_dot_ptq1_0_q8_1_multi, which is correct. #215 replaces that same #else with return 0.0f, which I have flagged there as a regression that silently zeroes PTQ1_0 on MUSA. Neither branch contains the other, and the two versions of this one function differ by about fifty lines, so they will conflict. Whoever resolves that conflict could reinstate the MUSA stub without noticing it is a defect, since it looks like an intentional part of the other change. Worth agreeing an order before either lands.

For the conflicts among the branches, we can merge the #211 first (it's hand-verified and device-proven, #215 is still open against prism), then #215 can rebase onto it. We need to inform the branch owner and make sure that the conflict resolver must preserve the vec_dot_ptq1_0_q8_1_multi call in the #else, the return 0.0f variant must not survive the merge. We can loop in the #215 owner i.e. @professorpalmer

@bri-prism Is this ok?

@bri-prism bri-prism left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent review: posted by the maintainer's coding agent at their request.

Approving at 3a82de9. Thanks for posting the device output. The 120/120 PTQ1_0 test-backend-ops pass on gfx1151 checks __builtin_amdgcn_perm against the CPU reference on real hardware, which is the evidence I was asking for. The only change since 85a6c5f is the pointer comment, so that run covers this head.

On sequencing: yes, merging this first and then rebasing #215 onto it works for me, with one condition for whoever resolves the conflict. #215 (and #221, which carries it) now widens this branch to #if defined(GGML_USE_HIP) || defined(GGML_USE_MUSA). This PR puts __builtin_amdgcn_perm inside that branch, and it is an AMD-only builtin, so keeping #215's condition over this body would break the MUSA build. After the rebase, the vectorized path needs to stay under GGML_USE_HIP only, with MUSA on the scalar loop, and the #else must not end up returning 0.0f for any backend that can reach it. @professorpalmer, flagging this for the #215 rebase.

Method note: source reading at 3a82de9 plus your posted device output. I did not build or run it.

@professorpalmer

Copy link
Copy Markdown

Understood, and thank you for spelling out the condition.

After #211 is on prism, I will rebase #215 onto it with this split in vec_dot_ptq1_0_q8_1:

#221 carries the same function, so the same split applies there on its next rebase. I will not rebase either branch onto this one before it lands.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants