TT Lab
はじめる
学ぶ 学習パス コース

コンピュータ構成

命令一つに62秒かかることもある — 遅延は経路の属性だ

TT Labで続きを見る

一言でいうと

「この命令は何サイクルか」という質問は、周辺の状態を固定しなければ答えがありません。レイテンシは命令の属性ではなく、命令とシステムの状態の組み合わせに対する観測値です。

なぜ必要なのか

最適化を学ぶと、まず命令のレイテンシ表を暗記します。加算は1サイクル、乗算は3サイクル、除算は20サイクル。この表は役に立ちますが、その数字を命令の固定された性質だと信じた瞬間、実際の性能を説明できなくなります。

2026年に公開された「Assembly Hall of Shame」というランキングが、この点を極端に示しています。サブタイトルは「CPU性能の底を目指す競争」で、命令1つを最も遅くする競争の記録です。最下位のnopが1サイクル、1位のfxrstor64が1,980億サイクルで62秒です。同じ種類の中で2,000億倍の差があるなら、その種類を1つの数字で測っているという前提から疑う必要があります。

どう動くのか

そのランキングを下から上へ読むと、現代のCPUが止まりうる地点の一覧になります。3つの区間に分かれます。

1つ目は、コアの中でマイクロコードが介入する地点です。ハードウェアの高速経路が処理できない入力を与えると、制御がマイクロコードのルーチンへ移ります。faddは、正常なオペランドでは数サイクルですが、オペランドが非正規化数(denormal)だと677サイクルかかります。命令はそのままで、データだけが変わったのに、2桁の倍率が生じます。

2つ目は、コヒーレンシとプラットフォームが関与する地点です。アトミック演算のオペランドがキャッシュラインの境界にまたがると(スプリットロック、split lock)、高速なキャッシュコヒーレンシの経路を使えず、外部バスのロックをかける必要があります。865サイクルかかり、その間、他のコアまで影響を受けます。キャッシュ全体を空にするwbinvdは160万サイクルですが、これは命令が複雑だからではなく、その時点でキャッシュに入っていたダーティなデータをすべてDRAMまで押し出さなければならないからです。

3つ目は、ダイの外に出る瞬間です。上位はすべてmovです。データを移す以外には何もしない命令が、1秒以上かかります。理由はアドレスにあります。DRAMではなく、PCIeファブリックの先のデバイスレジスター(MMIO)を読んでいるからです。MMIOの読み取りは、応答を受け取って終わるノンポステッドトランザクションなので、往復時間がそのまま実行時間になります。

1位の戦略が決定的です。512バイトの状態を、最も遅いMMIO領域から復元させると23秒になります。ここに、他のコアが別のMMIOレジスターを叩き続けるようにすると、62秒になります。つまり、命令1つの実行時間が、他のコアが何をしているかによって、2倍以上変わったのです。

現場での姿

この極端な遊びの結論は、ごく普通の性能改善の作業にそのまま当てはまります。

マイクロベンチマークは、命令ではなくシステムの状態を測っています。 同じコードでも、キャッシュが温まっているときと冷えているとき、他のコアが遊んでいるときと忙しいときとで、まったく違う数字を出します。ベンチマークの数値を引用するときに条件を一緒に書かなければならない理由です。

最悪のケースは、平均の倍数ではありません。 上の区間は連続的ではなく、崖で分かれています。平均のレイテンシがどれだけ良くても、崖を1つ踏むと、そのリクエスト1つが100倍遅くなります。テールレイテンシを扱うときは、平均を改善するよりも、崖を見つけてなくすほうが効果的です。

実務で実際に踏むことになる崖は、3つです。値が0に収束する信号処理コードの非正規化数、アラインメントを気にしなかったアトミック演算のスプリットロック(Linuxカーネルのsplit_lock_detectが、デフォルト値warnで警告を出してくれます)、そしてデバイスレジスターをループの中で読むMMIOポーリングです。

続くクイズで確認すること

「このコードはなぜ昨日は速く、今日は遅いのか」という質問に、命令の一覧ではなく、経路と状態で答えられるかを確認します。