ARM64_Lab

PowerShell 7 の起動が Windows PowerShell 5.1 より遅かった話(どちらも ARM64 ネイティブ)

この記事の見出し
  1. 7回だけ起動して比べた
  2. 数字はかなり揺れる
  3. 中央値の取り方でやらかした
  4. 遅い理由をARM64には押しつけない
  5. 対話と自動化で分ける

pwsh -NoProfile -c exit の中央値は386.8ms、powershell -NoProfile -c exit は212.2ms だった(起動だけを見ると、思ったより差が大きい)。

PowerShell 7 のほうが新しいのだから、少なくとも起動だけなら同じくらいだろうと思っていた。2026年8月2日に Surface Pro 11th Edition で測ると、結果は逆になる。Snapdragon X Elite X1E80100、Windows 11 Pro 10.0.26200 の ARM64 版、AC 接続、電源プランは「バランス」(GUID 381b4222-f694-41f0-9685-ff5bb260df2e)という、普段の作業に近い条件。

最初に疑ったのはエミュレーションだった。けれど PE ヘッダーを読むと、pwsh も powershell.exe も Machine が 0xAA64。どちらも ARM64 ネイティブで、片方だけ x64 だから遅い、という逃げ道はなかった(ここで外していたら記事の方向がかなり変わっていたと思う)。

7回だけ起動して比べた

Measure-Command で7回まわし、初回と2回目以降を分けて記録した。ファイルキャッシュやランタイムの状態で初回は揺れやすいので、全部をひとつの中央値にしてしまうと読みにくい(この手の起動時間は、最初の1回だけ妙に重くなることがある)。

1..7 | ForEach-Object {
    (Measure-Command { pwsh -NoProfile -c "exit" }).TotalMilliseconds
}

-NoProfile は外せない条件にした。自分の pwsh プロファイルには補完や見た目まわりの設定が入っているので、それを読ませると「PowerShell 7 が遅い」のか「自分のプロファイルが重い」のか分からなくなる。OneDrive の同期が落ち着いてから始めたが、常駐を全部消したクリーンな測定ではなかった。

数字はかなり揺れる

初回を含めた7回分を、そのまま表に残しておく。

コマンド 初回 2回目以降の中央値 全7回
pwsh -NoProfile -c exit 535.3ms 386.8ms 535.3 / 361.2 / 563.7 / 454.2 / 324.5 / 300.4 / 386.8
powershell -NoProfile -c exit 284.6ms 212.2ms 284.6 / 183.6 / 185.4 / 263.8 / 211.9 / 212.2 / 217.3
python -c pass 64.3ms 58.0ms 64.3 / 38.7 / 58.0 / 42.4 / 40.9 / 90.2 / 131.9
node -e 0 110.8ms 95.9ms 110.8 / 135.8 / 80.3 / 84.4 / 94.4 / 100.5 / 95.9
git --version 106.9ms 91.6ms 106.9 / 80.4 / 97.8 / 58.9 / 91.6 / 104.3 / 71.9

pwsh は300.4ms から563.7ms まで振れている。最速と最遅で1.9倍あるので、1回だけ測って「こっちが速い」と決めるのは危ない。powershell.exe も183.6ms から284.6ms まで動くが、それでも中央値では212.2msに収まった(この揺れを見て、7回でも多すぎるとは感じなくなった)。

ついでに測った python、node、git は、起動だけならかなり軽い。python は58.0ms、node は95.9ms、git は91.6ms。普段「PowerShell のワンライナーが少し重い」と感じていたのは、勘違いだけではなかったらしい(同じ短いコマンドでも、入口の重さがかなり違う)。

中央値の取り方でやらかした

今回いちばん危なかったのは、最初の測定スクリプトをそのまま信じかけたことだった。3回測って中央値を取るつもりで、こう書いていた。

$sorted = $ms | Sort-Object
$median = $sorted[[int]($n / 2)]

$n が3のとき、3 / 2 は1.5になる。PowerShell の [int] は銀行家丸めなので、1.5 は2へ丸められる。3要素の配列で添字2は最大値。つまり中央値ではなく、こっそり最悪値を拾っていた。

出てきた数字が体感より重く見えて、そこでやっと気づくことになった。[math]::Floor() に直し、回数も7回へ増やして測り直したのが上の表。エラーも警告も出ないので、この手のバグはかなり嫌らしい(答えの形だけはそれっぽく見える)。

遅い理由をARM64には押しつけない

pwsh は .NET のランタイムを立ち上げてから動く。手元の .NET SDK は 9.0.316 で、dotnet --info の RID は win-arm64。ランタイム側もネイティブなので、「ARM64 だから遅い」という話にはしにくい。

むしろ PowerShell 7 の起動構造そのものの差、と見るほうが自然だった。x64 機でも pwsh の起動が Windows PowerShell 5.1 より重いという話は珍しくないし、今回の386.8ms対212.2msも、その延長に見える。とはいえ、ARM64 Windows 上で実際にこの差が出たことは、短い自動化では無視しにくい。

1日1回だけ走るスクリプトなら、この差はほとんど気にならない。けれどタスクスケジューラや小さな補助コマンドで1日100回呼ぶと、17秒ぶん余計に待つ計算になる。ここまで積むと、気持ちの問題だけでは済まなくなってしまった。短い処理ほど本体より起動の割合が大きくなるので、シェルを選ぶだけで妙に効いてくる場面がある。逆に、長い処理ならこの差はすぐ埋もれるので、起動時間だけを見て全部を powershell.exe に寄せるのもまた違うし、対話で使う場所、タスクスケジューラから呼ぶ場所、他のツールが内部で呼ぶ場所を分けて考えないと判断ミスになった。差は残った。

対話と自動化で分ける

対話的に使うシェルは、今も pwsh のままにしている。補完、履歴、表示の感じが違うので、毎日触る場所を Windows PowerShell 5.1 に戻す気にはならなかった。起動時に386.8ms払っても、開きっぱなしならその後はほとんど気にならず、実際の対話操作では待たされる感じは薄い。対話で使う場所、タスクスケジューラから呼ぶ場所、他のツールが内部で呼ぶ場所を分けて考えないと判断ミスになった。

一方で、短いスクリプトを大量に呼ぶ用途は powershell.exe に寄せた。書いている処理が 5.1 の範囲で足りるなら、起動が半分近くで済むほうを選ぶ。2026年7月末に自動化を組み直したとき、pwsh と powershell.exe が混ざっていた箇所を、この基準で整理した。呼び出し元がタスクスケジューラなのか、別の CLI からの補助処理なのか、手元で開きっぱなしにする対話シェルなのかで、同じ386.8msでも意味が変わる。用途を分ける。入口だけ見た。測って決める。

補足すると、この差はあくまで起動時間だけの話。ループを回す、ファイルを舐める、API を叩く、といった処理本体で pwsh が遅いと感じた場面は、少なくとも手元の作業ではまだ出ていない。測る対象を「何もしないで exit する」ところに絞ったから見えた差で、日常の作業全体を212.2msと386.8msだけで語るのは無理がある。

測り直してよかった、というのが最後に残った感想。なぜ最初の表を疑えたのかといえば、体感より少し重すぎる数字に見えて、そこで一度立ち止まれたからだった(この小さな違和感がなければ、そのまま出していた可能性がある)。最初の中央値バグをそのままにしていたら、もっと大げさな差として書いていたはずで、数字がそれっぽいときほど疑う必要がある。最初はどちらを採用するか迷ったが、測定の失敗を潰してから判断するほうを選び、同じ測り方をもう一度試してみた。

a
arm64lab — 個人運営

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