PERF: `-ftrapping-math` slows down `np.less` `float32` loops on Apple AArch64
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 48/100
- issue の種類
- バグ
- 明瞭さ
- おおむね明確
- 活発さ
- 静か
- 技術スタック
- python
- 領域
- performance
調査の方向性
まず、提供されている np.less および関連する ufunc のベンチマークを Apple AArch64 で再現し、-ftrapping-math を付けたビルドと付けないビルドを比較します。ビルドフラグが no_float_errors および SIMD 比較とどのように相互作用するかを調査し、その後、フラグを削除または制限しても、divide の例を含む浮動小数点エラーの挙動が維持されるかを確認します。必要な例外処理を変更せずに、測定可能なパフォーマンス向上が得られれば完了です。
索引モデルが issue の本文から書いたものです。
説明
Describe the issue:
Default numpy 2.5.1 build passes -ftrapping-math to every compiled file.
That numpy version is built with apple clang 15 (based on llvm 16), which is where AArch64 started honoring this flag.
With this flag, llvm scalarizes comparisons, because the exception flags for vectorized and scalar comparisons are different.
But for fcmeq this is unnecessary (llvm issue). And in comparisons that use fcmge and others, numpy sets no_float_errors=True,
so apart from hurting performance and toggling NPY_SIMD_GUARD_PARTIAL_LOAD, this flag does nothing for these ufuncs.
If you remove scalarization, for example by forcing -fno-trapping-math(spin build --clean -- -Dc_args=-fno-trapping-math -Dcpp_args=-fno-trapping-math) to measure
(NPY_SIMD_GUARD_PARTIAL_LOAD won't change because no-trap doesn't cancel -DNPY_HAVE_CLANG_FPSTRICT), you'll get:
Here the time is measured for 5e8 elements:
lambda f: min(timeit.repeat(f, repeat=10, number=int(5e8/n)))
| n | type | func | trap | no-trap |
|---|---|---|---|---|
| 100000 | float32 | less | 0.1024202 | 0.0326608 |
| 100000 | float32 | less_equal | 0.1025368 | 0.0305651 |
| 100000 | float32 | add | 0.0309067 | 0.0326873 |
| 100000 | float32 | minimum | 0.0307194 | 0.0317346 |
| 100000 | float32 | fmin | 0.0307535 | 0.0329900 |
| 100000 | float32 | sin | 0.5513217 | 0.3256507 |
| 100000 | float32 | exp | 0.5220611 | 0.5224958 |
| 100000 | float32 | sqrt | 0.0616653 | 0.0617320 |
| 100000 | float64 | less | 0.0898140 | 0.0646568 |
| 100000 | float64 | less_equal | 0.0897867 | 0.0639634 |
| 100000 | float64 | add | 0.0880477 | 0.0912926 |
| 100000 | float64 | minimum | 0.0790273 | 0.0892458 |
| 100000 | float64 | fmin | 0.0792423 | 0.0884805 |
| 100000 | float64 | sin | 1.2245625 | 1.2364713 |
| 100000 | float64 | exp | 0.6959067 | 0.6961037 |
| 100000 | float64 | sqrt | 0.1165173 | 0.1165576 |
| 10000000 | float32 | less | 0.1020606 | 0.0473397 |
| 10000000 | float32 | less_equal | 0.1021814 | 0.0473080 |
| 10000000 | float32 | add | 0.0509410 | 0.0516631 |
| 10000000 | float32 | minimum | 0.0515318 | 0.0516390 |
| 10000000 | float32 | fmin | 0.0515197 | 0.0516493 |
| 10000000 | float32 | sin | 0.5514753 | 0.3254238 |
| 10000000 | float32 | exp | 0.5212769 | 0.5213362 |
| 10000000 | float32 | sqrt | 0.0609508 | 0.0609891 |
| 10000000 | float64 | less | 0.0918725 | 0.0962532 |
| 10000000 | float64 | less_equal | 0.0925714 | 0.0962611 |
| 10000000 | float64 | add | 0.1034192 | 0.1042124 |
| 10000000 | float64 | minimum | 0.1044532 | 0.1040570 |
| 10000000 | float64 | fmin | 0.1043755 | 0.1036816 |
| 10000000 | float64 | sin | 1.3308548 | 1.3272675 |
| 10000000 | float64 | exp | 0.6959297 | 0.6958054 |
| 10000000 | float64 | sqrt | 0.1157437 | 0.1157453 |
Note that you can't do this, because divide will set the invalid flag on tail elements, although the data will be correct. But there's no divide in the benchmark, so the measurement is valid.
import numpy as np
np.seterr(all='raise')
for n in range(1, 33):
a = np.arange(1, n + 1, dtype=np.float32)
a / a
with -fno-trapping-math it gives FloatingPointError: invalid value encountered in divide.
So the question: is it necessary to set the flag everywhere, even for no_float_errors, since it impacts performance?
Measurements were on clang 21, apple m5, numpy 2.6.0.dev0. On apple clang 15 the measurements are slightly different, but the regression remains.
Reproduce the code example:
import numpy as np, timeit
print("n,type,func,time")
for n in (int(1e5), int(1e7)):
for t in (np.float32, np.float64):
a = np.random.rand(n).astype(t)
b = np.random.rand(n).astype(t)
cb = np.empty(n, np.bool)
cb.fill(0)
cf = np.empty(n, t)
cf.fill(0)
bench = lambda f: min(timeit.repeat(f, repeat=10, number=int(5e8/n)))
for f in (np.less, np.less_equal):
print(str(n) + "," + str(t.__name__) + "," + str(f.__name__ )+ "," + f"{bench(lambda: f(a, b, out=cb)):.7f}")
for f in (np.add, np.minimum, np.fmin):
print(str(n) + "," + str(t.__name__) + "," + str(f.__name__) + "," + f"{bench(lambda: f(a, b, out=cf)):.7f}")
for f in (np.sin, np.exp, np.sqrt):
print(str(n) + "," + str(t.__name__) + "," + str(f.__name__) + "," + f"{bench(lambda: f(a, out=b)):.7f}")
Error message:
Python and NumPy Versions:
2.6.0.dev0+git20260729.4350526
3.14.4 (v3.14.4:23116f998f6, Apr 7 2026, 09:45:22) [Clang 17.0.0 (clang-1700.6.4.2)]
Runtime Environment:
[{'numpy_version': '2.6.0.dev0+git20260729.4350526',
'python': '3.14.4 (v3.14.4:23116f998f6, Apr 7 2026, 09:45:22) [Clang 17.0.0 '
'(clang-1700.6.4.2)]',
'uname': uname_result(system='Darwin', node='MacBook-Pro.local', release='25.4.0', version='Darwin Kernel Version 25.4.0: Thu Mar 19 19:33:43 PDT 2026; root:xnu-12377.101.15~1/RELEASE_ARM64_T8142', machine='arm64')},
{'simd_extensions': {'baseline': ['NEON', 'NEON_FP16', 'NEON_VFPV4', 'ASIMD'],
'found': ['ASIMDHP', 'ASIMDDP'],
'not_found': ['ASIMDFHM', 'SVE']}},
{'ignore_floating_point_errors_in_matmul': True}]
How does this issue affect you or how did you find it:
I found a discrepancy in the speed of np.sin between 2.5.1 (clang 15) and 2.6.0 (clang 21), went to investigate and found that they both scalarize comparisons.
- 主要言語
- Python
- スター
- 32.8k
- フォーク
- 12.8k
- 平均マージ
- 1日 7時間
- マージ済み PR(30日)
- 197
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
numpy/numpy のほかの issue
-
00 - Bug
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
-
04 - Documentation sustain-2026
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
-
04 - Documentation
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
似ている issue
-
難易度 1/5 1時間未満 初心者へのやさしさ 90/100
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 86/100
zostera/django-bootstrap4#894 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
use-agent-os/agent-os#3276 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
zephyrproject-rtos/zephyr#119726 ·
-
area/auth bug comp/agent P3 platform/discord type/security
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
NousResearch/hermes-agent#117848 ·