Skip to content

Q4_K GEMV has no aarch64 SIMD and its "parallel" variant calls the scalar path #2567

Closed

Description

@noahgift

The hottest kernel in quantized inference is scalar and single-threaded on every ARM
machine
, silently. Found by the aarch64 dogfood before the 0.64.0 cut (#2566).

crates/aprender-compute/src/backends/q4k/gemv/mod.rs:53 — #[cfg(target_arch = "x86_64")]
wraps the entire fast path: the >=8M-work parallel dispatch (exactly what catches
FFN 8960x1536 and lm_head 151936x1536), AVX-512, and AVX2. On aarch64 control falls
straight through to scalar::matmul_q4k_f32.

Line 203 is the part worth reading twice:

#[cfg(not(target_arch = "x86_64"))]
fn matmul_q4k_f32_parallel(...) { scalar::matmul_q4k_f32(...) }

A function named parallel that directly calls the serial scalar one.

Structural census (measured on origin/main)

  • aprender-serve/src: 183 target_arch = "x86_64" sites vs 16 aarch64
  • backends/q4k/ and backends/q6k/: zero aarch64 cfgs
  • grep -rn sve: nothing

Why no gate caught it

The numbers are correct — the 339-row tensor table on GB10 is byte-identical to x86.
Only the speed is wrong. Every correctness gate passes; there is no ARM speed gate at all,
so this could not have been caught by anything currently running.

Work

  1. NEON path for Q4_K GEMV (and Q6_K, same shape).
  2. Make matmul_q4k_f32_parallel actually parallel on non-x86 — the rayon dispatch is
    ISA-independent and is being skipped for no reason.
  3. A speed gate that runs on ARM. Without it this regresses silently again, and the
    naming (_parallel calling scalar) shows how easy that is.

Not release-blocking for 0.64.0 — correctness is unaffected — but it means ARM users get a
fraction of the advertised throughput with nothing telling them.

Refs #2566

Activity

  1. added
    pp-067PP-066 spec §6: 0.67 lane (carried row)
    on Sep 5, 2026
  2. noahgift commented on Sep 5, 2026

    @noahgift
    MemberAuthor

    0.67 lane — attaches to PP-066 spec §6 row W-G.

    The parallelism half landed (PR #2689, 1.21× on GB10, measured in nightly-bench.yml); the SIMD half — a NEON Q4_K/Q6_K GEMV — is verbatim §6 W-G ('GB10: NEON Q4_K GEMV'). The ARM speed gate it asks for is the gx10 cell (master row 15) plus the #2567 nightly measurement.

    Instance of class #2880 — the shipped CPU path runs a narrower ISA or algorithm than the host supports.

    PP-066 triage 2026-09-05 (issue #2873); ledger docs/audits/pp-066-triage-2026-09-05.md.

  3. noahgift commented on Sep 10, 2026

    @noahgift
    MemberAuthor

    Closed by paiml-implement — #2880

  4. noahgift commented on Sep 10, 2026

    @noahgift
    MemberAuthor

    Reopened by the PMAT-1100 triage orchestrator: #2567 is a child (sub-issue) of epic #2880, not a duplicate of it — the lane's close was wrong. Filed to a train.

  5. added this to the 0.68.0 milestone on Sep 10, 2026
  6. modified the milestones: 0.68.0, 0.70.0, 0.73.0, 0.73.0 on Sep 16, 2026
  7. noahgift commented on Sep 23, 2026

    @noahgift
    MemberAuthor

    triage 2026-09-23 (aprender-dd, standing triage owner): milestoned to 0.73.0, theme llama.cpp Parity (performance, kernels, parity benches); it moved out of backlog (the 2026-09-17 overflow bucket, not a release).

  8. noahgift commented on Sep 24, 2026

    @noahgift
    MemberAuthor

    Closed by paiml-implement — folded into epic #3999 (triage evidence/triage-1717.md)

  9. added a commit that references this issue on Sep 28, 2026
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

    pp-067PP-066 spec §6: 0.67 lane (carried row)

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions