Skip to content

Template Metal complex scalar lanes - #2

Draft
PhysicistJohn wants to merge 1 commit into
mainfrom
draft/generic-metal-complex
Draft

Template Metal complex scalar lanes#2
PhysicistJohn wants to merge 1 commit into
mainfrom
draft/generic-metal-complex

Conversation

@PhysicistJohn

@PhysicistJohn PhysicistJohn commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Motivation

MLX Metal currently defines complex storage and arithmetic only through the float-specific complex64_t, while MLX CUDA already uses the lane-generic complex_t<T> = cuda::std::complex<T>. This PR independently gives the Metal backend the same extensible type foundation while preserving all current complex64 behavior and kernel entry points.

It complements upstream #3969, which independently templates the Metal C2C FFT scalar lane. Together, the two narrowly scoped changes establish the orthogonal type and FFT foundations needed to evaluate reduced-precision complex support in MLX. That closes a capability gap with accelerator stacks that already provide reduced-precision complex transforms: NVIDIA cuFFT supports FP16 and BF16 complex transforms, and AMD rocFFT supports FP16 complex transforms.

Neither PR introduces a user-facing dtype or commits MLX to promotion, dispatch, or accuracy semantics; those remain separate design decisions.

Summary

  • Replace the float-specific Metal complex structure with lane-generic complex_t<T>.
  • Preserve every existing kernel entry point through using complex64_t = complex_t<float>.
  • Generalize scalar and cross-complex conversions, arithmetic, and Limits for packed half and bfloat lane types.
  • Add compile-time layout and reduced-lane arithmetic checks.

Scope boundaries

This patch intentionally does not add public complex32/complex16 dtypes, host promotion rules, Python API, production half/BF16 kernel instantiations, FFT integration, or FP8. Those should be separate proposals after this primitive is accepted.

The current Metal compiler accepts the template implementation but rejects C++20 concept declarations, so mixed scalar operators use a narrow enable_if alias for equivalent compile-time constraints.

Compatibility checks

Check Result
Release Metal build Pass
Minimum-target Metal compile/link (macos-metal2.4, macOS 14.0) Pass
CTest, serial 261/261 pass
Existing complex64 output Bit-identical for 8 operators across 65,536 deterministic values
Exported Metal symbols Exact match: 15,692 lines and identical SHA-256
Existing complex64 latency Add -0.49%, multiply +0.06%, divide -0.10%; no material regression
Metallib size +13,440 B (+0.01026%); public metadata and function list unchanged

The metallib increase is confined to module and reflection metadata. No new public kernel symbols are emitted.

Reduced-lane proof

Both complex_t<half> and complex_t<bfloat16_t> are exactly 4 bytes. A linked on-device probe produced the expected (-2, 14) result for both types.

LLVM AIR inspection shows native half and bfloat loads, stores, arithmetic, division, and FMA instructions. The reduced paths do not promote their internal arithmetic to float.

A focused on-device streaming complex multiply-add benchmark over 2^24 elements measured:

Lane type Median kernel time Relative to float
float 0.8095 ms 1.00x
half 0.3819 ms 2.12x faster
bfloat 0.3826 ms 2.12x faster

This is a raw-type bandwidth-oriented microbenchmark, not an end-to-end MLX dtype performance claim.

Replace the float-specific Metal complex struct with complex_t<T> while retaining complex64_t as an alias and preserving existing kernel entry points.

Generalize scalar and cross-complex conversions, arithmetic, and limits so half and bfloat lane types retain packed two-lane storage without implicit float promotion.
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