Skip to content

Add ASV benchmarks for add and subtract, including complex in-place - #292

Open
antonwolfy wants to merge 1 commit into
mainfrom
asv-add-subtract
Open

antonwolfy wants to merge 1 commit into
mainfrom
asv-add-subtract

Conversation

@antonwolfy

Copy link
Copy Markdown
Collaborator

The ASV suite has no benchmark for add or subtract and no micro-benchmark for a complex dtype. The contiguous complex fast path in #278 makes complex add/subtract up to 2.4x faster, and the suite doesn't see it: its only complex coverage is the mandelbrot kernels, which moved by 0–2% with that change. This PR adds benchmarks that do.

Changes

  • micro/bench_micro.py: BenchAdd and BenchSubtract, written like BenchArctan2 and BenchPower (a new result array on each call), for float32, float64, complex64 and complex128 at 10k, 100k and 1M. The 10k and 100k sizes run mkl_umath's own loops and 1M calls oneMKL VM, since the threshold is n > 100000. A _uniform helper draws the real and imaginary parts independently.
  • micro/bench_inplace.py (new): BenchAddInplace and BenchSubtractInplace for complex64 and complex128 at 10k and 100k, with modes out=a, out=b and z += z. Each mode takes its own loop in perf: add contiguous fast path to complex add/subtract fallback loop #278. z += z starts from zeros, because repeated doubling would overflow to inf.
  • README.md: coverage table.

That's 48 new benchmarks, 24 per file. On the host below they add about 30 s per pass.

Testing

Core Ultra 5 236V (Lunar Lake), conda-forge icx 2026.1.1, NumPy 2.5.4, MKL 2026.1.0, MKL_NUM_THREADS=1, pinned to one P-core with turbo off. asv run --python=same against two builds of main 8ca254c (byte-identical, as an A/A control) and #278 at 1346ce2, with passes ordered main, main, #278, #278, main, main and samples appended.

Median time relative to main, so below 1 is faster:

Benchmark #278 / main
BenchAdd / BenchSubtract, complex at 10k and complex64 at 100k 0.70–0.79
BenchAdd / BenchSubtract, complex128 at 100k 0.98
BenchAdd / BenchSubtract, complex at 1M (oneMKL) 0.99–1.00
BenchAdd / BenchSubtract, float32 / float64 0.94–1.07 (A/A spread 0.93–1.04)
In place, complex64 0.42–0.66
In place, complex128 at 10k, and z += z at 100k 0.54–0.79
In place, complex128 out=a / out=b at 100k 1.03–1.10, not significant per ASV

The complex128 in-place cases at 100k use two 1.6 MB arrays, more than the 2.5 MB L2, and ASV's spread there is ±5–10 µs. The same pattern outside ASV (fresh setup, one warm-up call, one timed call, 1,800 samples per build) gives 0.90–0.92 with an A/A of 1.00, so I read the ASV result as noise at that size.

Median times

Medians of 20 single-call samples, turbo off.

dtype size add main add #278 subtract main subtract #278
float32 10k 3.26 µs 3.30 µs 3.47 µs 3.25 µs
float32 100k 24.6 µs 24.5 µs 24.6 µs 24.5 µs
float32 1M 666 µs 658 µs 669 µs 664 µs
float64 10k 6.60 µs 7.07 µs 6.93 µs 6.56 µs
float64 100k 62.0 µs 65.9 µs 62.7 µs 63.2 µs
float64 1M 1.316 ms 1.320 ms 1.299 ms 1.298 ms
complex64 10k 9.20 µs 6.42 µs 9.18 µs 6.55 µs
complex64 100k 84.9 µs 66.8 µs 85.6 µs 66.9 µs
complex64 1M 1.260 ms 1.242 ms 1.285 ms 1.275 ms
complex128 10k 13.3 µs 10.1 µs 13.5 µs 10.0 µs
complex128 100k 249 µs 245 µs 251 µs 245 µs
complex128 1M 2.651 ms 2.655 ms 2.628 ms 2.605 ms
dtype size mode add main add #278 subtract main subtract #278
complex64 10k out=a 7.93 µs 5.08 µs 7.75 µs 5.07 µs
complex64 10k out=b 7.82 µs 4.96 µs 7.75 µs 5.05 µs
complex64 10k z += z 6.74 µs 3.46 µs 6.65 µs 3.50 µs
complex64 100k out=a 75.1 µs 48.4 µs 75.3 µs 48.8 µs
complex64 100k out=b 74.2 µs 48.7 µs 74.1 µs 48.5 µs
complex64 100k z += z 62.2 µs 25.9 µs 60.3 µs 26.1 µs
complex128 10k out=a 12.9 µs 9.98 µs 12.8 µs 9.94 µs
complex128 10k out=b 12.7 µs 10.1 µs 12.8 µs 9.97 µs
complex128 10k z += z 11.3 µs 6.13 µs 11.0 µs 6.19 µs
complex128 100k out=a 143 µs 152 µs 139 µs 152 µs
complex128 100k out=b 144 µs 148 µs 144 µs 148 µs
complex128 100k z += z 114 µs 61.4 µs 104 µs 61.7 µs

pre-commit passes on the changed files.

Notes

  • Unrelated, found while checking coverage: BenchArctan2 and BenchPower time stock NumPy. arctan2 and power are commented out in generate_umath.py, so patch_numpy_umath() leaves them alone. power gets mkl_umath loops in Add oneMKL power loops for float32 and float64 #285; both are left as they are here.

The ASV suite had no benchmark for add or subtract and no micro-benchmark
for a complex dtype, so changes to those loops, such as the contiguous
complex fast path in gh-278, did not show up in it.

Add BenchAdd and BenchSubtract to micro/bench_micro.py, written like
BenchArctan2 and BenchPower, for float32, float64, complex64 and
complex128 at 10k, 100k and 1M elements. Add micro/bench_inplace.py with
BenchAddInplace and BenchSubtractInplace for complex in-place calls
(out=a, out=b and z += z) at 10k and 100k, where mkl_umath runs its own
loops instead of oneMKL VM.
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