読者です 読者をやめる 読者になる 読者になる

解き放たれしソフトウェア

GNU/LinuxなどFLOSSについて書いてみるつもり

Firefox を PGO でビルドする【Gentoo Linux】

FirefoxProfile Guided Optimization (PGO) でビルドすると、実行速度が速くなるのですが、ビルドに2倍くらいは時間がかかります。 PGO は、1度ビルドしてみる→実行して情報収集→その情報でより速いバイナリにビルド、という手順を踏むため、おそらく2倍以上かかります。

Firefox が遅いと言われることはしばしばありますが、遅い理由としては、1) 機能がリッチなブラウザなのにマルチプロセスを活かしていない*1、2) 遅いバイナリを使用している、3) 遅いアドオンが入っている*2、などといった原因があると思われます。

世の中の Firefox のほとんどはビルド済バイナリで配布を受けたものでしょうけれども、これでは多くのマシンで動作する汎用のバイナリなので比較的遅いです。 "Gentoo Linux"では、ビルド済バイナリでをインストールすることも可能ですが、手元でビルドすることも可能です。 そのため、 PGO でのビルドが少しは身近になっています。 しかし、ビルドに何時間もかかるため、気軽にというわけではありませんが。

Gentoo での PGOビルドではプロファイルの情報収集に、 X.Org サーバの Xvfb を利用して、実際のスクリーンに開かず仮想のディスプレイを利用します。

メモリとディスクが多く必要

gentoo リポジトリFirefoxebuild ファイルでは、/var/tmp/portage の容量に 8GB を要求しています。 「多ければ 8GB 要る」ということですので、なんとか用意してください。/var/tmp/portage を tmpfs にしているなど RAM を使用していて RAM で 8GB に足りないならば、スワップアウトを想定して swap も確保し、 mount -o size=8g /var/tmp/portage などしてファイルシステムを 8GB 以上にします。

また、そもそもビルドにメモリが沢山要ります。コンパイルよりも特にリンクの方が、一時的にメモリを大量占有します。メモリが数ギガバイト未満程度だとリンクに失敗するかもしれません。

"pgo" USE フラグ

まず第一に、${USE} で pgo を有効にします。Portage だと /etc/portage/package.useとかで。 www-client/firefox pgo

このように設定ファイルに書けば恒常的に有効になりますが、Portage については variable *3 を渡して実行することも可能なので、emergeebuild に USE="pgo" を渡すことも可能です。

Ccache を無効にする

もし Ccache を使用しているのであれば、PGO では使わない方がよいと思います。 少なくとも、2周めのビルドではナンセンスでしょう。

Portage では、パッケージ単位で ${FEATURES} を変更するには /etc/portage/package.env/etc/portage/env/ 以下を利用します。

なお、 ${FEATURES} は "incremental variable" で、つまり FEATURES="-ccache" と書けば足ります。むしろ FEATURES="${FEATURES} -ccache" と書くとまずいです、 FEATURES="${FEATURES// ccache/ -ccache}" なんて、必ず失敗します。

また ${FEATURES} に関しても、 FEATURES="-ccache"emergeebuild に渡すことも可能なはずです。

けどデフォルトでは失敗した

そこまでしても私のところでは、デフォルトの ${CFLAGS} ${CXXFLAGS} ${LDFLAGS} では失敗しました。

これらの variable も、 /etc/portage/package.env などで変えられます。

また、 www-client/firefox の ebuild では、これらを自動的に書き換える(ユーザによる指定を無効にする)ようになっているため、 USE フラグの custom-cflags custom-optimization を有効にすることも要します。

${CFLAGS} ${CXXFLAGS} の -O レベル

GCC での optimization 設定には、 -O[level] のショートハンドがあり、一般的に利用されていると思います。

このオプティマイズレベルですが、手元では -O0 または -O1 だと成功しました。 本当は -O2-O3 にして速くしたいのですが、何らかの理由でうまくいきませんので*4原因究明中です。 -O0 だとオプティマイズ無しなので物凄く遅いバイナリになるため、 -O1 が私のいまのところの妥協点です。

CFLAGS="-O1 -march=native -pipe"

CXXFLAGS=${CFLAGS}

上記でおおよそは足りるはずです。

なお、最近の FirefoxC++11 で書かれていますが、 ebuild ファイルの方で -std=gnu++11 を追加してくれるのでこの点では支障ありません。

${LDFLAGS} で確実にリンクする

結論からいうと、LDFLAGS="-Wl,--no-as-needed -Wl,--sort-common -Xlinker --no-keep-memory" で成功しました。

リンク速度よりもメモリ消費を減らすことと、直接には必要ないと思われるシンボルでも消さないこと、-Wl,-O1 のようなオプティマイズはしないことが必要でした。

また、 -Wl,-z,now も追加するとセキュリティ強化になりますが、起動時にライブラリを全てロードすることになるので、起動が遅くなります。(デフォルトの -Wl,-z,lazy の方が速いです。)

まとめ

こうしてみると、ビルドをすんなり通すためには大量のメモリが要るのだと思います。十数ギガくらいかも。そうしないと、最後のリンクで転けます。 もっとオプティマイズすると速くなるはずなのですが、サイズが大きくなるのですよね。

とはいえ、gcc と g++ に '-O1' 、 ld に '-Wl,-z,lazy' でビルドしたもので、顕著に速くなりました。

ちなみに、「Clang だとビルドが速くなるのでは?」と思うでしょうが、コンパイラはまだしも、リンカは ld.bfd でないとうまくいきませんでした。 最近の GCC も速いので、そんなに差がないのではないかと思います。

*1:これは Electrolysis (e10s) で解消します

*2:例えば Ad Block Plus (ABP) は、 uBlock Origin よりも遅いです。

*3:通俗の訳は「変数」ですが、適訳は「可変値」

*4:メモリが足りないのかもしれないし、よく判らない