Skip to content

Split ARM64 prefetches at the cache line size - #9599

Merged
alexreinking merged 1 commit into
alexreinking/simplify-div-bounded-offsetfrom
alexreinking/arm-prefetch-line-size
Oct 26, 2026
Merged

alexreinking merged 1 commit into
alexreinking/simplify-div-bounded-offsetfrom
alexreinking/arm-prefetch-line-size

Conversation

@alexreinking

Copy link
Copy Markdown
Member

reduce_prefetch_dimension splits every ARM prefetch into 32-byte pieces, which is the smallest cache line size of 32-bit ARM cores. 64-bit ARM cores have lines of at least 64 bytes, and Apple's cores have 128-byte lines. On Apple silicon, 3 of every 4 prfm instructions therefore request a line that was already requested.

This PR splits prefetches at:

  • 128 bytes for 64-bit ARM on macOS and iOS;
  • 64 bytes for other 64-bit ARM targets;
  • 32 bytes for 32-bit ARM, as before.

correctness_prefetch used to check only the host target. It now also lowers its tests for x86-64-linux, arm-32-linux, arm-64-linux, arm-64-macos, and arm-64-ios.

Benchmarks

The benchmark is an int8 smmla gemm from the GGML app (#9511), with a Func::prefetch of the weight stream added at offsets 2, 4, and 8 iterations of the K loop. Each region is 128 bytes per iteration. Times are on an M3 Max, relative to the same kernel without the prefetch (lower is better), min of 3 runs:

K×N×M, threads 32 B (before) 128 B (this PR)
4096×4096×32, T1 1.055–1.057 0.994–0.998
4096×14336×32, T1 1.056–1.085 1.011–1.028
14336×4096×32, T1 1.043–1.087 1.002–1.031
4096×14336×128, T1 1.023–1.031 0.979–0.984
4096×4096×32, T10 1.043–1.059 0.994–1.003
4096×14336×32, T10 1.053–1.059 0.995–1.007

With the old split, the extra prefetch instructions made the kernel 3–9% slower. With this PR they cost nothing measurable. (The hardware prefetchers already cover this kernel's weight stream, so prefetching doesn't make it faster either.)

Stacked on #9520.

Checklist

  • Tests added or updated (not required for docs, CI config, or typo fixes)
  • Documentation updated (if public API changed)
  • Python bindings updated (if public API changed)
  • Benchmarks are included here if the change is intended to affect performance.
  • Commits include AI attribution where applicable (see Code of Conduct)

Validation: correctness_prefetch and correctness_bad_prefetch pass; the full ctest -L correctness run at the top of the stack passed 703/704 (the only failure is correctness_cross_compilation, because the local LLVM has no Hexagon backend).

@slomp slomp left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, with an experiment you can try.

Comment thread src/Prefetch.cpp
(t.os == Target::MacOS || t.os == Target::IOS)) {
// Apple's 64-bit ARM cores have 128-byte cache lines.
max_dim = 1;
max_byte_size = 128;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Out of curiosity, have you tried 256 instead of 128?

Context: not long ago I wrote a benchmark, and found out that even though the cache line size of Apple Silicon is 128 bytes long, for false-sharing scenarios, using 256 bytes gave better performance still.

Curious to see if it would have an effect here.

@alexreinking
alexreinking force-pushed the alexreinking/arm-prefetch-line-size branch from 60606d1 to bc53255 Compare October 24, 2026 15:43
@alexreinking
alexreinking removed this pull request from stack #9605 October 24, 2026 15:44
@alexreinking
alexreinking added this pull request to stack #9609 October 24, 2026 15:45
@codecov

codecov Bot commented Oct 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 73.05%. Comparing base (9638399) to head (bc53255).

Additional details and impacted files
@@                             Coverage Diff                              @@
##           alexreinking/simplify-div-bounded-offset    #9599      +/-   ##
============================================================================
+ Coverage                                     73.02%   73.05%   +0.03%     
============================================================================
  Files                                           263      263              
  Lines                                         82415    82422       +7     
  Branches                                      20156    20159       +3     
============================================================================
+ Hits                                          60181    60213      +32     
- Misses                                        15913    15919       +6     
+ Partials                                       6321     6290      -31     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

reduce_prefetch_dimension split every ARM prefetch into 32-byte pieces,
the smallest line size of 32-bit ARM cores. 64-bit ARM cores have lines
of at least 64 bytes, and Apple's have 128-byte lines, so most of the
emitted prfm instructions touched an already-requested line. Split at
64 bytes for 64-bit ARM and 128 bytes for 64-bit ARM on macOS/iOS; keep
32 bytes for 32-bit ARM.

correctness_prefetch now also lowers its tests for x86-64, 32-bit ARM,
64-bit ARM Linux, macOS, and iOS targets, not only the host.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@alexreinking
alexreinking force-pushed the alexreinking/arm-prefetch-line-size branch from bc53255 to ddbe3ea Compare October 26, 2026 19:58
@alexreinking
alexreinking merged commit f81c296 into main Oct 26, 2026
21 of 48 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants