同じループを Python と Node で回したら5倍差がついた(ARM64 Windows 実測)
この記事の見出し
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日動かし続けた状態で測っている。