Full-scan native Metal fitting on Apple M5#

2026-11-05. This measurement does not meet a two-second end-to-end target.

Workload#

Public ARINA gold, 512 × 512 scan positions and 192 × 192 detector pixels, on Apple M5 (10 GPU cores, 24 GiB unified memory). All scan positions and 7,644 selected bright-field pixels were retained. The existing median-fill policy repaired four detector marker pixels. No binning or precision reduction was introduced. Calibration: 300 kV, 30 mrad, 0.5 Å scan sampling, and 0.46 mrad detector sampling for the native benchmark.

Native sources match ceec1d9715e10fcbec276971b27163b6ce0c4055 with the typed central-difference compiler fix in ddbe7a1. Python transfer parity used a preserved working snapshot; only the transfer implementation changed between its matched before/after runs.

The maintained metal-direct-fit-benchmark ran three fits with 200 trials, Newton refinement, fifth-order refinement, and the default half-band check. All three returned identical fitted optics, tilt, depth, and higher-order coefficients. This is repeatability evidence, not an independent calibration or cross-backend correctness claim.

Stage (seconds)

First observation

Repeat 1

Repeat 2

Fourier preparation

3.749

22.461

10.120

Joint-model preparation

3.944

10.040

3.294

Initial fit

9.832

8.887

8.699

Fit through fifth order, including initial fit

16.766

15.578

15.390

Original HDF5 loading was 6.9 s. Creating the reusable benchmark fixture took 28.3 s including that load and is separate from interactive fitting. Source pages were not cold-cache controlled. System memory pressure affected the preparation measurements; these are active-desktop observations, not an idle hardware limit. The runner’s current top-level load_s includes subsequent work; the separated numbers above use its stage records and load log instead.

Candidate evaluation experiment#

The maintained objective benchmark evaluated identical fixed candidate sets, three repetitions each. Reducing candidates per Metal thread from four to two preserved every recorded agreement exactly, but made low-order batches slower:

Batch

Original GPU ms/candidate

Two candidates/thread

8 candidates

26.201

33.024

64 candidates

26.195

34.354

The candidate was rejected. Trial counts, half-band checks, fit bounds and precision remain unchanged. No fitting speedup is claimed.

Validated fixes and remaining gates#

  • Torch-to-MLX transfer now blits from the Metal buffer handle without requiring host mapping of private storage. Contiguous, offset and transposed MPS tensors passed exact transfer tests. Six matched real-data Phase reconstructions (256 × 256 × 128 × 128, tilt and no tilt) matched the original phase hashes and fitted parameters exactly.

  • Splitting a central-difference expression into typed Swift intermediates resolves a compiler type-check failure without changing its arithmetic. Three physical-Metal fifth-order gradient/search tests passed.

  • The Python benchmark-direct command still rejects the current staged optimizer because its guard expects the earlier GPU-only Newton metadata. Its failed invocation is not a qualifying timing or a CPU-fallback measurement.

  • Native application acceptance and source-to-visible timings must be reported separately. Kernel fitting does not certify a complete UI workflow or a release. The two-second full-size target remains open.

Native application observations#

The native app loaded the full 512 × 512 × 192 × 192 acquisition with scan and detector binning both 1. Two successful load observations reached confirmed resident presentation in 9.764 s and 4.525 s; source-cache state was uncontrolled. Other attempts were cancelled by system memory-pressure warnings. A local UI fix replaces the stale loading percentage with an explicit stopped/retry state.

The app’s BF-edge calibration measured 0.6035446 mrad/pixel and selected 7,647 BF pixels. These differ from the benchmark settings above, so the following times are not a matched overhead comparison. Source calibration was not rewritten. The app reported an initial fit in 33.231 s and a separately requested fifth-order refinement in 41.488 s, publishing the refined image state at 34.993 s before its checks completed. The final state contained all 200 primary trials, order 5, a 2048 × 2048 Auto output, matching requested/shown revisions, and a trusted half-band result (0.23 rad). Endpoint Metal allocations were 14.122 GB; this is not a peak-memory measurement.

Native actions used opt-in test hooks. Screenshot permission was unavailable, so visible-window acceptance remains pending. The app currently requires the separate fifth-order refinement action. No end-to-end two-second claim, native release qualification, or automatic-fifth-order UI claim follows from this test.

A replay with the corrected native UI harness completed both stages. Resident presentation took 5.134 s. Initial Run-to-result was 19.502 s including automation waits; app timers reported 3.883 s preparation and 14.883 s fitting. The separate fifth-order image state was published at 2.122 s and its checks completed at 6.047 s (7.029 s including automation waits). Run through both stages was 26.650 s, excluding acquisition loading and calibration setup. Final phase hash and fitted coefficients matched the earlier app run exactly. These warmer observations are not an optimization speedup.

The corrected harness reports workflow_pass=true, visual_evidence_pass=false, and overall pass=false: all three screenshots were denied by macOS. Metal allocations sampled after the initial fit were 17.999 GB, falling to 14.122 GB after refinement. Neither sample is a measured peak; this does not qualify a 16 GiB memory limit.

Private evidence ID: mps-full-scan-20261105.