Skip to content

Add Arm SVE CPU backends - #2021

Draft
hughcars wants to merge 6 commits into
CEED:mainfrom
hughcars:sve-instructions
Draft

Add Arm SVE CPU backends#2021
hughcars wants to merge 6 commits into
CEED:mainfrom
hughcars:sve-instructions

Conversation

@hughcars

@hughcars hughcars commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Add Arm SVE resources for serial and blocked CPU execution:

/cpu/self/sve/serial
/cpu/self/sve/blocked

The new resources delegate general CPU behavior to /cpu/self/opt/* and replace tensor contraction and supported non-tensor basis application with vector-length-agnostic SVE implementations.

Closes #1984.

What changed

  • Add one FP32/FP64 SVE tensor contraction.
  • Retain output accumulators across the reduction dimension.
  • Use four-vector, two-vector, and predicated one-vector tiles.
  • Handle runtime vector length and inactive tail lanes without fixed-width assumptions.
  • Use the same contraction for bounded scalar non-tensor basis maps.
  • Preserve the existing strided path for multi-component layouts and long reductions where flattening is not beneficial.
  • Detect SVE support with a compile probe using the active compiler target and flags.
  • Retain weak unavailable-backend registration when SVE is not enabled.
  • Add guard-page, finite-result, vector-tail, and non-tensor dispatch-boundary tests.
  • Add GCC/Clang and FP32/FP64 SVE coverage to the Arm workflow.

The implementation uses the SVE instruction subset. It does not require SVE2 and does not add SME support.

There is no runtime ISA dispatch. A library built with SVE enabled must run on an SVE-capable target.

Scope

This PR does not add operator or restriction overrides, exact-shape dispatch tables, fixed vector-length policies, or workload-specific dispatch.

The generic even-odd work in #2010 is complementary. Its half-sized contractions can use this backend through the existing CeedTensorContractApply interface without an SVE-specific implementation.

Performance

Graviton4 / Neoverse V2 with 128-bit SVE, using paired CPU-pinned medians over 16 libCEED-only Palace-weighted H1 and H(curl) operator cases:

Comparison FP64 FP32 Combined
Speedup over /cpu/self/opt/blocked 4.89× 7.09× 5.89×
SVE time relative to the best matching backend 1.079× 1.196× 1.136×

LIBXSMM was the best matching backend in all 16 cases. The SVE backend was 13.6% slower overall.

The held-out ratio was 1.079. Portable BP coverage passed 28 cases with a maximum relative error of 1.33e-7.

These are libCEED operator measurements rather than complete Palace application timings. The result is a substantial improvement over the generic opt path, not LIBXSMM parity.

Validation

Submitted head:

8a8e52b0a3c3bfaadfccbc3a8d5bab3dbaf2f227

Validation included:

  • Full Spack build and test suite.
  • GCC and Clang.
  • FP32 and FP64.
  • Serial and blocked SVE resources.
  • Native Graviton4 execution.
  • QEMU SVE at 128, 256, 384, 512, and 2048 bits.
  • Guard pages around tensor inputs and outputs.
  • Transpose overwrite and accumulation.
  • Nonfinite-result rejection.
  • Tensor sizes around one-, two-, four-, and eight-vector boundaries.
  • Non-tensor dispatch boundaries at:
    q_comp*Q = 254, 256, 258
    num_elem = 4*VL-1, 4*VL, 4*VL+1
    
  • Scalar and multi-component non-tensor bases.
  • Held-out, portability, and wrapper/scaling checks through eight workers.
  • Clang 22 formatting and git diff --check.

The Arm workflow explicitly verifies that SVE was enabled before running both resources. It covers GCC and Clang, FP32 and FP64, and checks the emulated vector length before each QEMU run.

LLM/GenAI disclosure

OpenAI GPT-family coding agents did substantial implementation and analysis work on this PR. They proposed and edited code, built the benchmark and test scaffolding, debugged failed variants, analyzed generated instructions and measurements, added CI coverage, reviewed the final diff, and helped draft this description.

The vector and row tiling, unroll settings, 256-entry cutoff, and 4*VL batch gate came from an automated benchmark campaign rather than manual derivation. Variants were retained only when they passed the correctness, held-out, portability, and performance gates.

GPT-6 Astra and Claude Fable 5.1 performed separate read-only reviews. Their findings led to stricter finite-result checks, broader vector-tail coverage, explicit non-tensor dispatch-boundary tests, and SVE-required CI.

Validation was automated as well. The submitted head was tested with GCC and Clang, FP32 and FP64, both SVE resources, native Graviton4 hardware, and QEMU vector lengths from 128 through 2048 bits. The full Spack suite and the retained performance, held-out, portability, and scaling checks passed.

Model transcripts, experiment histories, benchmark output, profiles, and other generated artifacts are not included in the patch.

Exercise transpose and accumulation semantics at lengths around the runtime vector width. Protect array boundaries on Linux so inactive vector lanes cannot access memory.
Add serial and blocked resources that delegate to the corresponding opt backend and provide a vector-length-agnostic tensor contraction. Detect SVE support from the active compiler target and retain predicated tails in both scalar precisions.
Apply scalar non-tensor basis maps as contiguous contractions when the reduction is bounded or the element batch fills the four-vector tile. Use the standard strided contraction for all remaining component layouts. The 256-entry transpose cutoff is an empirically selected cache and scheduling boundary.
Reject nonfinite contraction results, cover vector-tile and non-tensor dispatch boundaries, and exercise both SVE resources under GCC and Clang in both precisions across multiple emulated vector lengths.
Set the linux-user default vector length as well as the supported maximum so each QEMU run executes at the requested width. Print the width, resource, and test before each invocation.
@jeremylt

Copy link
Copy Markdown
Member

I'm a bit worried about seeing that the SVE tensor contraction logic is leaking into the SVE Basis? It would be really good if we could use the base CPU ref Basis implementation and only have special case logic in the TensorContract object.

Keep architecture-specific behavior in the TensorContract object and use the shared opt/ref Basis implementation. Non-tensor basis applications continue to dispatch through the SVE contraction via CeedTensorContractStridedApply.
@hughcars

hughcars commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

That makes sense. I removed the SVE Basis override in b97b1292b. The shared ref/opt Basis still reaches CeedTensorContractApply_Sve through CeedTensorContractStridedApply; this only removes the SVE-specific q_comp * Q flattening policy.

The TensorContract-only version passed the full suite, GCC/Clang, FP32/FP64, native Graviton4, QEMU from 128–2048 bits, and focused H1, H(div), and H(curl) tests. Correctness was unchanged.

Metric Previous Shared Basis Change
Primary SVE / best 1.136 1.165 +2.5%
H1 / H(curl) 1.162 / 1.111 1.186 / 1.145 +2.0% / +3.1%
Held-out 1.079 1.111 +3.0%
Speedup over opt 5.89× 5.82× −1.2%
SVE source lines 624 464 −25.6%

The largest single regression was 12.8% in one FP32 non-tensor tet case, but the aggregate cost is about 2–3%. That seems worth the smaller backend and the cleaner boundary.

I also tested moving the flattening into CeedTensorContractStridedApply on a separate branch from main. The result was backend-dependent rather than a general win. Values below are geometric-mean runtime changes; negative is faster. The 16-case suite was used during SVE autoresearch, while the 22 held-out cases were excluded from tuning.

Backend 16-case suite (/blocked) 22 held-out cases (/blocked) 8 serial cases (/serial)
ref +5.0% +5.6% neutral
opt −0.4% neutral +3.6%
XSMM −1.7% −0.9% +3.5%

So I do not think the unconditional generic version is worth proposing. It would need a backend-owned cost decision or another TensorContract hook. I could take a look at that as a separate follow-up if you'd be open to it.

Comment thread tests/t366-tensor.c
/// Test tensor contraction semantics and vector-length tails
/// \test Test tensor contraction semantics and vector-length tails
//TESTARGS(only="cpu") {ceed_resource}
#if defined(__linux__)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

These two test files confuse me. Can you explain why the existing tests are insufficient and these two tests need to be executed by all backends?

steps:
- name: Environment setup
uses: actions/checkout@v5
- name: Install QEMU

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I was originally thinking of testing SVE via emulation of supporting hardware, like the IBM Power job. If this runs faster, then we should switch to this for both SVE and IBM Power CI.

FC: ""
SVE_OPT: -O2 -g -ffp-contract=fast -march=armv8-a+sve
run: |
make -j4 OPT="$SVE_OPT" build/t366-tensor build/t367-basis

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

all backends should be run against the full test suite in CI, not a subset of the tests

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.

SVE Backend

2 participants