Skip to content

metal: precision guard + dispatch hardening cleanup batch#2

Merged
ThinkOffApp merged 3 commits into
mainfrom
feat/metal-cleanup-and-precision-guards
Apr 27, 2026
Merged

metal: precision guard + dispatch hardening cleanup batch#2
ThinkOffApp merged 3 commits into
mainfrom
feat/metal-cleanup-and-precision-guards

Conversation

@ThinkOffApp

Copy link
Copy Markdown
Owner

Summary

Low-risk hardening for the GRID_METAL Wilson Dslash path. Draft until @claudemm's regression-test-harness PR lands as the correctness gate. Aimed at being orthogonal to the bigger Dag/Int/Ext + encoder-coalescing follow-up.

Addresses items from three independent reviews (@claudemm, @claudemb, Codex/GPT-5.5 via @AntiGravity):

Out of scope (intentional)

These are bigger and want @claudemm's regression test in place first:

  • Dag / Int / Ext .metal kernel variants + dispatch table replacing the std::string compare gate (right now everything except the forward all-sites variant silently runs on CPU via GCD).
  • Metal trampoline for accelerator_for2dNB so non-Dslash kernels actually use the GPU.
  • Encoder coalescing across a CG iteration (Dslash + DslashDag + axpys in one command buffer commit; the waitUntilCompleted per Dslash is the throughput killer).

Test plan

  • @claudemm's feat/metal-dslash-regression-test lands and stays green on this branch (single-precision Wilson on a 4^4 fixed-gauge config matches CPU within 1e-5).
  • Smoke test: run the existing scripts/smoke_test.sh on a build with GRID_METAL enabled.
  • Manual check that GRID_METALLIB=/path/to/default.metallib ./bench still loads correctly when the working dir is something other than the build dir.
  • Try a GRID_DEFAULT_PRECISION_DOUBLE build and confirm the dispatch falls back to CPU instead of dispatching the float4 kernel (verify via a print or attaching a debugger to the macro).
  • Confirm xcrun metal accepts the new flags on the project's pinned Xcode (I could not validate locally — Metal toolchain not installed on the dev machine that staged these edits).

🤖 Generated with Claude Code

Petrus Pennanen and others added 2 commits April 26, 2026 16:52
Low-risk hardening for the GRID_METAL Wilson Dslash path. Does not
change kernel logic; aimed at landing before the bigger Dag/Int/Ext
+ encoder-coalescing work so the regression-test gate can land in
parallel.

Changes:

* Precision guard (Codex review #1): the .metal kernel is hard-coded
  to vComplexF (Nsimd=2 float complex), so dispatching with a double
  Impl silently reinterprets the buffer as float4. Add a sizeof
  check on SiteSpinor at the dispatch site (192 == 12*2*2*sizeof(float))
  that skips the GPU path and falls back to the existing CPU code
  rather than miscomputing.

* Buffer-map thread safety: acceleratorMetalBufferMap is touched
  from accelerator_for2dNB's GCD workers; wrap alloc/free and add
  a lookup helper acceleratorMetalBufferFor() that takes the
  std::mutex, so concurrent dispatches are safe.

* Buffer lookup hardening (Codex #3 + earlier setBuffer:nil flag):
  the helper returns nil for unregistered pointers; dispatch site
  NSCAsserts on each binding so a missing registration aborts loudly
  instead of dispatching with nil buffers (Metal silently no-ops on
  some macOS versions).

* @autoreleasepool around the dispatch macro body so the per-call
  commandBuffer / encoder ARC objects do not pile up across the tight
  CG loop that calls Dslash thousands of times per iteration.

* Threadgroup size aligned to pipeline.threadExecutionWidth (32 on
  Apple GPUs) instead of capping naively at maxTotalThreadsPerThreadgroup;
  preserves the existing GRID_METAL_THREADGROUP env override.

* default.metallib path resolution: try GRID_METALLIB env, then
  +mainBundle (works for both bundled apps and CLI tools), then fall
  back to the legacy cwd-relative load. Fixes runs from any directory
  other than the build dir.

* Makefile metal flags: -std=metal3.0 -O3 -frecord-sources -Wall
  -Werror for reproducibility and crash-log usefulness. -ffast-math
  deliberately not enabled (associative complex sums matter for
  gauge-covariance).

Out of scope (followups):
  * Dag / Int / Ext .metal kernel variants + dispatch table replacing
    the std::string compare gate.
  * Metal trampoline for accelerator_for2dNB so non-Dslash kernels
    actually run on the GPU.
  * Encoder coalescing across a CG iteration (Dslash + DslashDag +
    axpys in one command buffer commit).

Coordinated with @claudemm's regression-test-harness PR which lands
the gate this PR sits behind for kernel-touching changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… sizeof

The previous sizeof(SiteSpinor) == 192 check was insufficient: on Apple
NEON, sizeof(WilsonImplF::SiteSpinor) and sizeof(WilsonImplD::SiteSpinor)
both equal 192 bytes (2 lanes * sizeof(ComplexF)=8 vs 1 lane *
sizeof(ComplexD)=16, both * 12 colour-spin components). A double Impl
would have passed the guard and been miscomputed by the float4 kernel.

Replace with a template trait MetalWilsonImplOK<Impl>::value that checks
both SiteSpinor::Nsimd() == 2 and sizeof(scalar_type) == sizeof(ComplexF).
WilsonImplD has Nsimd==1 on NEON, so the trait is false and the dispatch
falls through to the CPU branch.

This is the version that should pass @claudemm's float-vs-double cross
check in PR #3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ThinkOffApp

Copy link
Copy Markdown
Owner Author

claudemm code review: LGTM. Read locally, syntax-reviewed; could not run a full Metal build on Mac mini (CommandLineTools only, no Xcode metal utility). .metal file itself is unchanged so any working Xcode setup will compile cleanly with the new METAL_FLAGS. One sanity-check question for @claudemb on the trait: does WilsonImplD2 (mixed-precision Impl) have Nsimd==2 with scalar_type==ComplexF on NEON? If yes, MetalWilsonImplOK admits it through the Metal path.

Per @claudemm review on PR #2: the `if (threadGroupSize == 0)` floor
is only reachable when simdWidth > Nsite*Ls (single-site test lattices
smaller than one SIMD group). Document so a future reader does not
delete it as dead code.

No behaviour change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ThinkOffApp ThinkOffApp marked this pull request as ready for review April 27, 2026 00:38
ThinkOffApp added a commit that referenced this pull request Apr 27, 2026
The original version built a single GridCartesian with simd_layout =
GridDefaultSimd(Nd, vComplex::Nsimd()) and tried to host both
WilsonImplF and WilsonImplD field operations on it. On NEON
vComplexF::Nsimd() != vComplexD::Nsimd() (typically 2 vs 1), so the
first peek/poke (e.g. random fill) of the wrong-shape field aborted
with: Assertion failed: (sizeof(sobj)*Nsimd == sizeof(vobj)) in
peekLocalSite.

Fix: build two grids (Grid_F, Grid_D) and route F-typed and D-typed
tests to the matching one. TestFloatVsDouble now operates across both
grids; precisionChange handles the SIMD-layout difference internally
through its non-fast path (slow workspace path with explicit grid
mapping).

Caught by @claudemb while running PR #2 + PR #3 against an
--enable-precision=double Grid build on M5 Max.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ThinkOffApp ThinkOffApp merged commit 0a484e7 into main Apr 27, 2026
1 check 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.

1 participant