コンパイラの最適化と聞くと、大規模なアルゴリズム変更を想像するかもしれない。しかし今回GCC(GNU Compiler Collection)のmainブランチにマージされた変更は、ファイル1枚の中の1行だけを書き換えたものだ。IntelのソフトウェアエンジニアであるLili Cui氏がコントリビュートしたこのパッチは、x86-64の汎用チューニングにおける分岐予測ミスのコスト評価を修正し、SPEC CPU 2017の分岐集約ベンチマーク(544.nab_r)でIntel Granite Rapidsが+12.7%、AMD Zen 5が+12.1%という性能向上を実証した。

なぜたった1行の変更がこれほどの差をもたらすのか。背景にあるのは、現代CPUのパイプライン構造とコンパイラのコストモデルの間に潜んでいた長年のズレだ。そしてこの変更が影響するのは、自分でコンパイラを設定する上級者ではなく、DebianやFedoraUbuntuのパッケージをそのままインストールするほぼすべてのLinuxユーザーである。

AD

分岐予測ミスが引き起こす「パイプラインの詰まり」

CPUがプログラムを実行するとき、命令をひとつずつ順番に処理するわけではない。現代のx86プロセッサは「パイプライン」と呼ばれる多段構造を採用しており、フェッチ・デコード・実行・書き込みといった処理を並列に進める。Intel Granite Rapidsのように20段前後のパイプラインを持つCPUは、理想的な条件であれば1クロックサイクルあたり複数の命令を完了できる。

ところがif文やwhile文のような条件分岐に差し掛かると、問題が生じる。CPUはその分岐がどちらに進むかを実際に計算する前に、「おそらくこちら」と予測して先読みを始める。この「分岐予測」が正解ならパイプラインは止まらない。予測が外れた場合(分岐予測ミス)は、パイプラインに詰め込まれていた途中の命令をすべて捨て、正しい経路から処理をやり直さなければならない。

このやり直しにかかるコストが「ミスペナルティ」だ。1990年代後半のx86プロセッサではパイプラインが5〜10段程度だったため、ミスペナルティも5〜10サイクル程度で済んでいた。現代のCPUは20〜30段以上の深いパイプラインを持つため、ミス1回あたり15〜20サイクル以上を無駄にする。

しかし、GCCの汎用コストモデル(generic)ではこのペナルティを「2命令相当のコスト」(COSTS_N_INSNS(2))と評価していた。2命令相当は現行CPUの実態では数サイクルにすぎず、実際のミスペナルティである15〜20サイクルとの間に大きな乖離が生じていた。この乖離こそが、コンパイラの最適化判断を長年にわたって誤らせていた根本原因だ。

データ依存の条件分岐が頻出するコード——たとえば分子動力学シミュレーションで原子間距離の閾値を繰り返し判定するような処理——では、このロスが積み重なって全体の実行時間に直結する。AMD Zen 5はZen 4以前と比べて1クロックあたり最大2分岐を予測できるよう強化されており、L1命令キャッシュへのスループットも改善されている。それでも予測「ミス」が発生したときのリカバリコストは依然として大きく、ミスそのものを減らす手段が求められていた。

1行の変更が引き起こす最大12%の性能向上:何が変わったのか

x86向けのコストモデルはgcc/config/i386/x86-tune-costs.hというファイルで定義されており、その中にbranch_mispredict_scaleというパラメータがある。「分岐予測ミスが発生した場合のコスト」を表す係数で、値が大きいほどコンパイラは「分岐は危険」と判断し、条件分岐を減らす方向の最適化を選びやすくなる。

今回のパッチ(コミット52cd02606b906160bf47001a00b446c35d46f15f、2026年6月24日)が行った変更はこうだ。

plaintext
// 変更前
.branch_mispredict_scale = COSTS_N_INSNS(2)

// 変更後
.branch_mispredict_scale = COSTS_N_INSNS(2) + 3

COSTS_N_INSNS(2)は「2命令相当のコスト」を意味し、それに3を加算することで実質「5命令相当のコスト」へ引き上げた。これにより、GCCはコードを最適化する際に「分岐予測ミスはもっと高くつく」と認識し、可能な場面では条件分岐を別の手段(if変換)に置き換えるよう動作が変わる。

コミットメッセージにはこう記されている。「この調整によりif変換が促進され、分岐予測ミスによるパイプラインストールを回避する」。

AD

if変換がパイプラインストールを回避する仕組み

CMOVとは何か

GCCが採用するif変換(if-conversion)とは、条件分岐命令を「CMOV(条件付きムーブ)」命令に置き換える最適化技術だ。if (a > b) x = 1; else x = 0;のようなコードは通常、条件分岐命令に変換される。CPUはこの分岐を予測しなければならず、予測が外れればパイプラインをフラッシュする。

x86-64にはCMOV(Conditional MOVe)という命令セットがあり、「条件が真なら値を移動する、偽なら移動しない」を1命令で表現できる。

plaintext
; 通常の分岐命令(簡略化)
cmp  a, b
jle  .else_branch
mov  x, 1
jmp  .end
.else_branch:
mov  x, 0
.end:

; CMOV命令を使ったif変換後(簡略化)
cmp  a, b
mov  x, 0
cmovg x, 1  ; a > b なら x = 1 に置き換え

CMOVのメリットとトレードオフ

CMOVは条件に依らず常に実行されるため、CPUは分岐予測を行う必要がない。パイプラインを止めるリスクがゼロになる点が最大の利点だ。

一方でトレードオフも存在する。CMOV命令は真の場合と偽の場合の両方の値を事前に計算する必要があるため、副作用のある計算(ゼロ除算の回避など)には使えないケースがある。if変換を増やすことでレジスタ使用量の増加やコードサイズの膨張が生じる場合もあり、一部のワークロードでは逆効果になる可能性がある点も無視できない。

コスト判定の仕組み

GCCが「if変換を適用すべきか」を判断する際の基準がまさにbranch_mispredict_scaleだ。このコストが低いままだと、コンパイラは「分岐してもそれほど損ではない」と判断してCMOV変換を見送る。コストを引き上げることで、GCCはより積極的にif変換を選択するようになる。今後、より多様なベンチマークでの検証が必要な変更でもある。

Granite Rapids +12.7%、Zen 5 +12.1%:ベンチマークが示す実態

テスト条件は-O2最適化フラグ、シングルコピー実行。このパッチの効果を示すデータとして、コミットにはSPEC CPU 2017の544.nab_rベンチマーク結果が添付されている。

CPU 性能向上率
Intel Granite Rapids(Xeon 6) +12.7%
AMD Zen 5(znver5) +12.1%

544.nab_r(Nucleic Acid Builder)は分子動力学シミュレーションをベースにした浮動小数点集約のベンチマークで、ANSI Cで実装されている。タンパク質の原子構造を処理し、原子間距離の閾値判定を繰り返す処理が中心で、データ依存の条件分岐が頻出する。こうしたパターンは分岐予測が困難であるため、if変換による最適化が特に効果的に働く。

ただし、この12%という数値はこのベンチマーク固有の結果だ。条件分岐の多さと予測難度によって恩恵の大きさは変わる。Phoronixも「他のワークロードへの影響は今後判明する」と記しており、汎用的な性能向上率として解釈する場合は注意が要る。

それでも、Intel/AMDの現行サーバー向けフラグシップで揃って10%超の向上が確認された事実には重みがある。Granite RapidsはXeon 6シリーズのRedwood Coveマイクロアーキテクチャを採用し、Zen 5はEPYC Turin/Ryzen 9000世代に相当する。この2アーキテクチャが同様の傾向を示したことは、深いパイプラインを持つ現代CPUにとってbranch_mispredict_scaleの調整が有効であることを裏付けている。

AD

「generic」というデフォルト設定の意味:なぜこの変更が広く影響するのか

GCCでx86向けのコードをビルドする際、-marchオプションで対象CPUを指定できる。-march=nativeを使えばコンパイル実行マシンのCPUに最適化された機械語が生成され、-march=alderlake-march=znver4のようにCPU固有のチューニングを選ぶこともできる。

-marchを明示的に指定しない場合、GCCは「generic」と呼ばれるデフォルトのチューニングを使う。「最も一般的なIA32/AMD64プロセッサ」向けに設計された汎用コストモデルで、特定のCPUに依存しない。GCCのバージョンが上がるたびに「その時点での標準的なCPU」を反映して更新される設計だ。

LinuxディストリビューションはほとんどのパッケージをこのGenericチューニングでビルドして配布している。

  • Debian: x86-64 v1相当(baseline)のgenericビルド
  • Fedora: x86-64 v2相当(SSE4.2対応以降)のビルドを採用
  • Ubuntu: GCC 15.2を26.04 LTSで採用済み。25.10/26.10でamd64v3パッケージ(AVX2対応以降)の展開が進む

これらすべてが今回のgenericコストモデル変更の恩恵を受ける対象になる。逆に、自分でソースからビルドして-march=nativeを指定しているユーザーにとっては影響がない変更だ——すでにCPU固有の最適化が適用されているからだ。

Phoronixはこの点を明示している。「This is for those just relying on the generic x86/x86_64 tuning and not any CPU-specific -march=native type builds.」

なお、Ubuntu 26.10でのamd64v3パッケージ展開(AVX2等の新命令の活用)と今回の変更は補完関係にある。前者が「使える命令セットを広げる」ものであるのに対し、今回の変更は命令セットとは独立してコスト計算の精度そのものを改善する。両者はレイヤーが異なる最適化だ。

GCCの次の一手と、ユーザーが恩恵を受けるタイミング

このパッチがマージされたのはGCC 17の開発ブランチ(mainline)だ。GCC 16は2026年4月30日にすでにリリースされており、今回の変更はGCC 16には含まれない。

GCC 17の開発スケジュールは以下の通りだ。

  • Stage 1(新機能受け入れ期間): 2026年4月23日〜
  • Stage 3(バグフィックスのみ): 2026年11月16日〜
  • Stage 4(リリース候補): 2027年1月18日〜
  • GCC 17.1リリース予想: 2027年春(4〜5月頃、過去のパターンから)

ユーザーが実際にこの最適化を享受できるのは、各ディストリビューションがGCC 17を採用してパッケージを再ビルドした後になる。Fedoraは通常GCCリリース後1〜数ヶ月以内に採用する傾向があるが、UbuntuでのGCC 17採用はUbuntu 27.04または27.10以降になる可能性が高い。正確なスケジュールはGCC 17の正式リリース後に各ディストリビューションが判断することになるため、現時点では推定の域を出ない。

今後の焦点は、Lili Cui氏(Intelのソフトウェアエンジニア、Gitアカウント: cuilili8868)がコントリビュートしたこの変更が、544.nab_r以外のワークロードでどのような結果をもたらすかだ。GCCコミュニティではGCC 15向けのx86バックエンド最適化でもIntelエンジニアチームが複数の貢献を行っており、genericチューニングの継続的な精度改善は今後も続くとみられる。

何も変えなくていい——distroアップグレードが1行の最適化を届けるまで

コンパイラはハードウェアが速くなっても、コストモデルが現実を正確に反映していなければ本来の性能を引き出せない。今回のGCCパッチは、その「認識のズレ」を1行で修正したものだ。

branch_mispredict_scaleの引き上げによって促進されたif変換は、Granite Rapidsで+12.7%、Zen 5で+12.1%という結果を544.nab_rで残した。この恩恵はディストリビューションのパッケージビルドを通じて広く届く——つまり、コンパイラを自分でチューニングする上級者ではなく、DebianやFedora、Ubuntuを普通に使うLinuxユーザーが受け取るものだ。

GCC 17の2027年春リリースと、その後の各ディストリビューションへの採用を経て、日常的なLinux環境のパフォーマンスが静かに底上げされていく。ユーザーが何も変えなくても、次のdistroアップグレードがその恩恵を届ける。