ARM64_Lab

同じループを Python と Node で回したら5倍差がついた(ARM64 Windows 実測)

この記事の見出し
  1. 測った条件
  2. 結果
  3. 起動時間を足すと絵が変わる
  4. 最初に出した数字は間違っていた
  5. この数字をどう使っているか
  6. 測り方の注意

1000万回の足し算に、Python は934.5ms、Node は185.9ms かかった。5.0倍の差だ。

言語の速度差としては知られた話で、驚くところではない。それでも自分の環境で測っておきたかったのは、ARM64 版の Windows で「片方だけエミュレーションが挟まっていて遅く見える」という可能性を潰したかったからだ。

結論を先に書くと、どちらもネイティブだった。差は純粋に実装の差になる。

測った条件

計測日は2026年8月2日。Surface Pro 11th Edition、Snapdragon X Elite X1E80100、Windows 11 Pro 10.0.26200 の ARM64 版。電源は AC 接続、電源プランは既定の「バランス」のまま。

Python は 3.12.10、Node は v24.13.0。両方とも実行ファイルの PE ヘッダーを読んで Machine が 0xAA64 であることを確認している。

回したのはこれだけ。

s = 0
for i in range(10000000):
    s += i
let s = 0
for (let i = 0; i < 10000000; i++) s += i

7回まわして、初回と2回目以降の中央値を分けて記録した。

結果

処理系 初回 2回目以降の中央値
Python 3.12.10 902.9ms 934.5ms
Node v24.13.0 235.9ms 185.9ms

Python は初回のほうが速いという、少し落ち着かない結果になった。902.9ms と934.5ms の差は3.5%ほどで、測定のばらつきの範囲に収まる。予想と違ったので何度か回し直したが、傾向は変わらなかった。7回では足りていない可能性が高い。

Node のほうは初回235.9ms から185.9ms へはっきり下がっている。JIT がループを最適化するまでに時間がかかり、2回目以降は暖まった状態で走る。教科書どおりの挙動だ。

起動時間を足すと絵が変わる

前に測った起動時間と合わせると、見え方が変わってくる。

  • Python: 起動58.0ms + ループ934.5ms = 992.5ms
  • Node: 起動95.9ms + ループ185.9ms = 281.8ms

Python は起動が速くて実行が遅い。Node は逆になる。

短いスクリプトを何百回も叩く用途なら Python が有利で、まとまった計算を1回まわすなら Node が有利、という分かれ方をする。手元では前者が多く、Python を選んでいる判断は変えていない。個人的には、この起動込みの比較のほうが日々の体感に近いと思っている。

最初に出した数字は間違っていた

正直に書くと、この表の値は2度目の測定によるものだ。

1度目に使った計測スクリプトは、中央値を取るつもりで $sorted[[int]($n / 2)] と書いていた。PowerShell の [int] は銀行家丸めなので、3回計測時の 3/2 が 2 に切り上がり、配列の最大値を拾ってしまう。中央値ではなく最悪値を並べた表になっていた。

Python が1000ms を超え、Node も250ms 近くに見えていたので、比率としては大きく変わらない。だからこそ気づくのが遅れた。壊れていても答えの形が自然だと、そのまま通してしまう。

[math]::Floor() に直し、計測回数も3回から7回に増やして測り直している。

この数字をどう使っているか

この5倍という比率は、ARM64 に固有のものではない。x64 機で同じコードを回しても似た比率が出るはずだ。

意味があるのは絶対値のほうで、Snapdragon X Elite の1コアで1000万回の加算に934.5ms かかる、という感覚を持っておけると見積もりが立つ。100万件のレコードを Python で1件ずつ処理するなら、それだけで100秒近い。そうと分かっていれば、最初から別の書き方を選べる。

数値処理を Python でやる場合、私は numpy に寄せた。手元に入っているのは 2.4.1 で、同じ1000万回の加算を np.arange(10_000_000).sum() に置き換えると中央値1.8ms で終わる。素の for ループの934.5ms に対して519倍だ。ループを回す話ではなくなる、というのはこういうこと。

測り方の注意

同じことを試す人向けに、引っかかった点を残しておく。

1つは、7回では足りない場合があること。Python 側は初回902.9ms に対して中央値934.5ms と、順序が逆転している。ばらつきが数%あるので、差が数%しかない比較には使えない。今回のように5倍の差があるケースだから成立している。

もう1つは、他のプロセスの影響を消していないこと。計測中も msedge は4840.3MB を確保したまま常駐しており、Copilot も1830.2MB を使っていた。クリーンな状態で測った数字ではなく、普段どおりの環境で出た数字だ。マシンを再起動して常駐を落とせば、もう少し良い値が出る可能性はある。最終起動が2026年8月1日13時32分で、そこから丸1日動かし続けた状態で測っている。

a
arm64lab — 個人運営

Surface Pro 11th Edition(Snapdragon X Elite)を2025年5月から常用機にしている個人の記録です。ARM64 版 Windows で詰まったところと、その場で測った値をそのまま書き残しています。特定の企業・団体とは関係がなく、いかなる組織を代表する見解でもありません。