Skip to content

[Phase 2] Add NEON SIMD path for CPU Adam on AArch64 - #8453

Merged
PKUWZP merged 5 commits into
masterfrom
mps-neon
Sep 14, 2026
Merged

PKUWZP merged 5 commits into
masterfrom
mps-neon

Conversation

@PKUWZP

@PKUWZP PKUWZP commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

Phase 2 of Apple Silicon support (follow-up to #8293/#8300/#8335): the CPU Adam kernel — the ZeRO-Offload optimizer path — ran scalar on AArch64 machines without SVE, which includes every Apple Silicon Mac. This adds a 4-lane NEON implementation of the existing SIMD macro layer.

Changes

  • csrc/includes/simd.h — a __NEON__ branch defining the full macro set (SIMD_LOAD/STORE/SET/ADD/MUL/FMA/SQRT/DIV/AND/ANDNOT/OR/XOR, width 4):
    • fp16 via the hardware converters (vcvt_f32_f16 / vcvt_f16_f32).
    • bf16 via the same round-to-nearest-even + NaN-quieting flow as the AVX512 store_16_f32_as_bf16_nearest (using vaddhn_u32 for the add-and-take-high-half step); loads are widen+shift.
    • x86 andnot(x, y) = ~x & y maps to vbicq(y, x) — operand order preserved (documented in a comment).
    • The bf16 simd_load/simd_store guards widen from AVX512-only to AVX512-or-NEON.
  • csrc/includes/cpu_adam.hStep_AVX's non-AVX512 bf16 bailout is lifted for NEON (this was silently sending bf16 back to the scalar tail); the two Adam gates widen to include __NEON__.
  • csrc/adam/cpu_adam_impl.cpp — same gate widening (4 sites, including kZenAdamAlign). The NEON branch sits before the existing __SVE__ alternative and they remain mutually exclusive builder-emitted defines.
  • op_builder/builder.pysimd_width() advertises -D__NEON__ for ARM_8 without SVE. 32-bit ARM keeps __SCALAR__: the vdivq_f32/vsqrtq_f32 intrinsics used are A64-only.
  • op_builder/mps/cpu_adam.py — switches from -D__SCALAR__ to -D__NEON__.
  • Lion/Adagrad/AIO gates are untouched and keep their current scalar behavior on ARM (candidate follow-ups).

Measured on Apple M5 Max (macOS 26.3, Apple clang, Homebrew libomp)

DeepSpeedCPUAdam step, 50M params, 10-step average, vs the -D__SCALAR__ build of the same tree:

dtype scalar NEON speedup
fp32 11.5 ms 3.8 ms 3.0×
fp16 11.5 ms 3.3 ms 3.5×
bf16 12.7 ms 4.9 ms 2.6×

Correctness

  • NEON and scalar builds produce bit-identical fp16 results on identical inputs (5 steps, 1M params).
  • All dtypes (fp32/fp16/bf16 params; fp32 and bf16 moments) match an fp32 torch.optim.Adam/AdamW oracle within storage rounding, at sizes exercising pure-SIMD, SIMD+scalar-tail (1000003), and sub-width (3) paths.
  • Existing suites on the M5 Max: test_cpu_adam.py + test_hybrid_adam.py + test_adamw.py — 122 passed, 7 skipped. The mps-torch-latest CI workflow JIT-builds this kernel in its offload configs, so the NEON path is exercised upstream on every touching PR.

The CPU Adam kernel ran scalar on AArch64 machines without SVE, which
includes every Apple Silicon Mac (the ZeRO-Offload optimizer path on
that platform). simd.h gains a 4-lane NEON implementation of the
existing SIMD macro set, following the AVX512 semantics: fp16 through
the hardware converters, bf16 through the same round-to-nearest-even
with nan-quieting flow as store_16_f32_as_bf16_nearest, and x86
andnot's operand order preserved. Step_AVX's non-AVX512 bf16 bailout
is lifted for NEON since the bf16 converters exist there.

Builders: simd_width() advertises -D__NEON__ for AArch64 without SVE
(the vdivq/vsqrtq intrinsics used are A64-only, so 32-bit ARM keeps
the scalar path) and the MPS cpu_adam builder switches from
-D__SCALAR__ accordingly. Lion/Adagrad gates are left untouched and
keep their scalar behavior.

Measured on an M5 Max (macOS 26.3, clang, libomp), DeepSpeedCPUAdam
step on 50M params, 10-step average, vs the -D__SCALAR__ build:
fp32 11.5 -> 3.8 ms, fp16 11.5 -> 3.3 ms, bf16 12.7 -> 4.9 ms.
NEON and scalar builds produce bit-identical fp16 results on identical
inputs; all dtypes match a fp32 torch.optim oracle within storage
rounding, including sizes exercising the SIMD/scalar tail boundary.

Signed-off-by: PKUWZP <zhipeng.rainbowserie@gmail.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-08T04:53:53.248131Z dfd9591 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@PKUWZP PKUWZP changed the title Add NEON SIMD path for CPU Adam on AArch64 [Phase 2] Add NEON SIMD path for CPU Adam on AArch64 Sep 8, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dfd95919b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread csrc/adam/cpu_adam_impl.cpp
Comment thread op_builder/builder.py
Comment thread csrc/adam/cpu_adam_impl.cpp
{
size_t rounded_size = 0;
#if defined(__AVX512__) or defined(__AVX256__)
#if defined(__AVX512__) or defined(__AVX256__) or defined(__NEON__)

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.

Use __NEON__ to filter may have impact on SVE device, @xylian86 to confirm.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@delock the two defines can't coexist: they're both emitted only by simd_width(), which checks 'sve' in cpuinfo flags first and returns -D__SVE__ there; -D__NEON__ is only returned for ARM_8 without sve, so an SVE device never sees the NEON define. In the source, every '#if ... or defined(NEON)' gate sits before the existing '#elif defined(SVE)' branch, and with NEON undefined those reduce to exactly the pre-PR expressions — the SVE dispatch, its Step_SVE path, and its log line are untouched. Happy to adjust if @xylian86 sees an SVE configuration I've missed.

cxx_args hardcoded -std=c++17, which lands after cpp_extension's own
-std flag and overrides it. torch 2.14's headers require C++20, so the
JIT build broke on CI runners with current torch while still compiling
against torch 2.13 locally. Drop the flag; cpp_extension supplies the
standard its headers need.

Signed-off-by: PKUWZP <zhipeng.rainbowserie@gmail.com>
@delock

delock commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Hi @PKUWZP , do you think using some different macro as NEON as path selection? NEON is built-in macro when running ARM compiler. I'm worrying that for ARM chips with SVE feature, the code here would direct C++ code into neon path which may not be intended. Use a macro slightly different would be a better choice.

create_adam_optimizer's diagnostic reported scalar arithmetic on NEON
builds; add the missing branch. The ZeRO-Offload tuning guide now
describes the NEON path for AArch64 CPUs without SVE instead of
claiming they stay scalar.

Signed-off-by: PKUWZP <zhipeng.rainbowserie@gmail.com>
Resolve op_builder/mps/cpu_adam.py: keep master's torch-version-aware
C++ standard selection (its fix for the same torch 2.14 header issue)
with this branch's -D__NEON__.

Signed-off-by: PKUWZP <zhipeng.rainbowserie@gmail.com>
@PKUWZP
PKUWZP added this pull request to the merge queue Sep 14, 2026
Merged via the queue into master with commit c102bbd Sep 14, 2026
15 checks passed
@PKUWZP
PKUWZP deleted the mps-neon branch September 14, 2026 03:56
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.

2 participants