2015年3月4日水曜日

普通にビルドするより速くなる? ChromiumのPGOビルド実験

PGO(Profile Guided Optimization: プロファイルガイド付き最適化)を使用してTungstenのBlinkエンジンをビルドしてみました。

ChromiumのPGOテストビルド版がほしい人は公式フォーラムのテスト版Tungstenで入手できます。

目次


PGOとは

PGOとは、プログラムを実行して関数の実行状況などの情報を収集し、それをもとに適切な最適化を行うことです。 これまでのLTCG(Link Time Code Generation: リンク時コード生成)よりも一歩踏み込んだ最適化ができます。

PGOビルドは、速度優先の最適化ビルドよりもコードサイズが小さく、サイズ優先の最適化ビルドよりも高速なコードが生成されるようになります。うまくプロファイルできれば、速度優先の最適化ビルドよりも高速になります。
一方で、プロファイル収集から最適化ビルドまでの工程が増えるため必然的にコストが増加することが欠点です。 また、プロファイル収集時の実行状況が実際に使用する状況と大きく異なる場合は、適切な最適化ができずに遅くなる可能性があります。

Firefoxでは、PGOを適用した非公式ビルドが多数存在する上に公式でも採用される動きがあるそうですが、Chromiumでは全く見つからなかったのでPGOビルドをやってみました。

2016/11/1 更新
ChromeもPGOビルドを行うようになりました。Chromiumでも同様のPGOビルドが可能かどうかは不明です。

おことわり

ChromiumのPGOビルドは実験段階で、正式には対応していません。 コンパイラやChromiumの不具合などによりビルドできないばかりか、正常に動作しない可能性があります。

2015/2/25時点で、x86版のみビルドできることを確認しました。x64版ではSaveRegisters_x86.asmのパッチを適用しないとプロファイル収集時にレンダリングプロセスが異常終了します(詳細)。
このパッチを適用してプロファイル収集を行うことはできましたが、pgomgrによるプロファイルのマージでfatal error PG0003が発生してしまってビルドできませんでした(下図)。原因はコンパイラ側と思いますが、いつ直るかは不明です。

ビルド方法

GYP_DEFINESの設定が異なる以外は、通常のビルドと同じです。 PGOの効果を最大限にするため、optimize=maxも追加しています。 大まかには、以下の通り。

  1. (ソースコードを入手)
  2. set GYP_DEFINES=buildtype=Official optimize=max chrome_pgo_phase=1
  3. gclient runhooks
  4. ninja -C src\out\Release chrome
  5. (プロファイル収集のためにchrome.exeを--no-sandboxオプションで実行、pgosweepでpgc書き出し)
  6. set GYP_DEFINES=buildtype=Official optimize=max chrome_pgo_phase=2
  7. python src/build/gyp_chromium
  8. ninja -C src\out\Release chrome

PGOビルドにあたって注意点をいくつか。

  • 通常のOfficialビルドではPGOの対象になるソースコードが限られる
    リンク時コード生成が有効でないコードはPGOの対象になりません。
    リンク時コード生成が有効になるOfficialビルドでも、ビルド時のメモリ節約のためパフォーマンスの影響が大きいもの (base、V8の一部、Blink(旧WebCore)の一部など)しか有効になりません。 PGOの効果を最大限に発揮するには、GYP_DEFINESにoptimize=maxを指定してください。
  • chrome.dllとchrome_child.dll以外は対象外(2015/2/25現在)
    実験中であることを考慮すれば、仕方のないことです。PGOの対象にしたい場合は、自分で.gypを書き換える必要があります。
    libEGL.dllやlibGLESv2.dll、pdf.dllを対象に入れるともっと効果的かもしれませんが、今回はそのままとします。
  • プロファイル収集が終わっても、そのまま終了してはいけない
    chrome_child!*.pgcが生成されないからです。終了前にpgosweepでchrome_child.dllのプロファイル情報を書き出しておきましょう。
  • 通常のOfficialビルドよりも非常に多くの時間、メモリ、ディスクを消費する
    詳細は下記「コスト」を参照。 特に、メモリ不足にご注意ください。何も考えずにビルドすると、24GBでは使い切ってしまいます。 大容量のメモリを搭載してページファイルを小さくしている方は要注意です。
  • プロファイルのマージ処理でpgomgr.exeがスタックオーバーフローになる可能性が高い
    この現象が発生した場合は、以下のコマンドのようにpgomgr.exeのスタックサイズを増やしてください。(以下の例では64MBに増量)
    editbin /STACK:0x4000000 pgomgr.exe
  • 効果があるかどうかは、プロファイル次第
    今回は試験目的ということで、JavaScript、DOM、Canvasのベンチマークを走らせたプロファイルを使用しましたが、 これが適切かどうかは検証していないのでわかりません。 少なくとも、Chromiumの全機能を網羅していないため、おそらく特定の領域しか最適化できていないでしょう。 用意したプロファイルが原因で、特定の処理が非常に遅くなる場合もあります。
    厳密には、もっと使用範囲の広いプロファイルが必要であり、影響を詳細に検証する必要もあるでしょう。
  • コンパイラの不具合により異常終了する確率が上がる
    PGOは積極的に最適化するので、コンパイラの不具合に遭遇する可能性が高くなります。 異常終了する場合は、PGOによる影響を確認し、影響が認められれば該当コードをPGOの対象外にするなどの対応が必要です。 しかし、原因特定が難しい場合があり、再現性がなければなおさらです。

どれぐらい高速になるのか

Chromium 40.0.2214.111 x86で、通常のOfficialビルド、Officialビルドにoptimize=maxを付けたビルド、 PGOビルド(これもoptimize=maxを付けた)の3つを比較しました。

ベンチマークテスト結果(トータルスコア)
Official (デフォルト) Official (optimize=max) PGO (optimize=max)
Peacekeeper 6637 6681 7782
Sunspider 1.0.2 145ms 144ms 143ms
Octane 2.0 39747 40388 41023
Dromaeo JavaScript 2364.14runs/s ±0.47% 2434.02runs/s ±0.21% 2421.56runs/s ±0.22%
Dromaeo DOM Core 2992.71runs/s ±0.53% 3128.47runs/s ±0.43% 3353.13runs/s ±0.58%
Kraken-1.1 938.3ms +/- 4.1% 932.4ms +/- 4.0% 897.9ms +/- 3.3%
Canvas Perf (GPU無効) 9.07 10.4 10.8
Canvas Perf (GPU有効) 9.65 9.69 10.0
Canvas Mark 2013 (GPU無効) 19217 23603 24792
Canvas Mark 2013 (GPU有効) 13032 17127 16994
Chalkboard HTML5 Benchmark (GPU有効) 17.27sec. 17.55sec. 15.75sec.

JavaScript、DOM、Canvas、SVGの速度を見るために、以上のベンチマークを走らせました。

数値上最も効果の大きかったのがCanvas Mark 2013(29%アップ)、次いでPeacekeeper(17%アップ)でした。 optimize=maxでビルドしたものよりも、DOMの全体的なパフォーマンスがよくなったようです。

一方で、ほとんど効果がなかったのはSunspider、Canvas Mark 2013(GPU有効)でした。 Dromaeo JavaScriptでスコアがそれほど伸びなかったのは、JITコンパイラが原因と思われます。(x86/x64のコード生成が動的で、C++コンパイラの最適化の影響が及びにくいため) ほか、GPUやGPU-CPU間のメモリ転送がボトルネックになっている疑いがあります。

表が膨大になってしまうのでトータルスコアのみ掲載しましたが、 個別スコアを見るとPGOにより改善したものもあれば、悪化したものもあるようです。
プロファイル収集でベンチマークプログラムを走らせただけですが、数値上ではよい結果が得られました。

ただし、この数値は体感速度向上につながっていません。PGOビルドであっても、 速くなったと感じられる状況は限られていると思います。

コスト

Official (デフォルト) Official (optimize=max) PGO (optimize=max)
ファイルサイズ(chrome.dll) 30.6MB 41.9MB 32.1MB
ファイルサイズ(chrome_child.dll) 33.7MB 39.5MB 33.0MB
ビルド所要時間 約1時間30分 約2時間10分 約5時間
(プロファイル収集込)
ビルド中間ファイルの合計サイズ 約30GB 約50GB 約55GB

2つのDLLファイルサイズはおおむね Official < PGO < Official(max) でした。
optimize=maxを指定した場合、PGOビルドしないとファイルサイズが非常に大きくなります。

次行はChromiumのビルドにかかった時間です。表のとおりで言うまでもないでしょう。 今回、プロファイルの取得は手動で行っているため、参考値としてください。

最後はChromiumをビルドするのに必要な容量です。 実際の消費量はソースコード(約10GB)も合わせたものになるのですが、含まれていません。 ビルドだけで60GB弱の領域が必要でした。

表には上げていませんが、上記[PGOビルドにあたっての注意点]のとおり、リンク時に大量のメモリを消費します。 物理メモリを24GB搭載していても、通常通り並列にビルドするとメモリを使い切ります。 搭載メモリが24GB以下の場合は、並列数を1にしてリンクすることを推奨します。

まとめ

  • ベンチマーク数値はDOMなどの一部項目が上昇
  • 体感速度向上は微妙
  • GPUやV8(JavaScriptのJITコンパイラ)などはPGOビルドでは効果がほとんど得られない
  • PGOビルドは通常ビルドよりも多くの時間とメモリが必要

コンパイラのバグによりx64版のPGOビルドができませんでしたが、バグが修正されたらx64版もPGOビルドを試してみたいと思います。

PGOビルドしたBlinkエンジンは、最初に書いた通りテスト版として公開されています。 通常版や他のChromium派生ブラウザと比べて体感速度がどう変わるか、試してみてはいかがでしょうか。