• 作成:

GHCの並列ビルドが実用的になっていました

状況が変わったのを感じました

以前にこういうメモを書きました。 StackはGHCやCabalに並列ビルドのjobsオプションを伝えていませんが, 伝えたとしてもGHCの問題で全然速くならない - ncaq

以前の記事では実際に測ってみてGHCの-jオプションは役に立たないことを確かめました。

しかし最近以下の記事を読みました。 GHC 9.8の新機能

これによると新しいGHCでは-jsemオプションを実装することで、対応したCabalなどのビルドツールでパッケージ単位とモジュール単位の並列化の両方のジョブ数を制御出来るようになったようです。つまりGHCのモジュール単位の並列化に意味があることをアテにした実装がなされています。

ということは今のGHCのバージョンでもモジュール単位の並列化は有効に動くのではないかと考えました。

とりあえず計測あるのみです。

計測

現在の最新であるStackageのLTS 21が利用するghc-9.4.5を使って測定しました。 GHC 9.8系を使わなくても、以前からモジュール単位の並列化が意味を成すものになっているのであれば、これでも有効なはずです。

環境

  • コンパイラ: GHC 9.4.5
  • OS: Ubuntu 22.04.3 LTS(WSL2)
  • CPU: Ryzen 9 7950X
  • メモリ: 128GB

計測結果

デフォルトと並列化有効化の時でそれぞれ当然ですがcleanしてからビルドして、 timeのtotalの値を見ます。

実際に仕事で使うモジュール並列化の速度を計測したいため、依存ライブラリは事前にビルドしておきます。

仕事で今メインで触っているパッケージ

Stackを使っているのでpackage.yamlghc-options-jを記載しました。

デフォルト

stack test --no-run-tests  167.03s user 6.98s system 101% cpu 2:51.65 total

-j有効化

stack test --no-run-tests  315.04s user 47.82s system 324% cpu 1:51.75 total

pandoc

モジュールが非常に多いパッケージとして選びました。 Cabalを使いましたが、 Stackにも対応しているのでそちらで測るべきだったかもしれません。

デフォルト

cabal build  276.47s user 4.18s system 100% cpu 4:40.59 total

-j有効化

cabal build --ghc-option='-j'  850.04s user 53.86s system 642% cpu 2:20.76 total

ビルドが高速化されました

仕事で使っているパッケージに関しても、モジュールが多いパッケージに関しても、ビルドが約2/3程度には高速化されました。

早速含めてコミットすることにしました。

16コア32スレッドを使い切ってくれないのはモジュール同士の依存関係が絡み合ってるのと、一つのモジュールにたくさん書いてしまうのが横行しているからでしょう。 cycle importの解決はかなり面倒ですし、これまではモジュールを分けても並列ビルドによる高速化が期待出来なかったので、速度面では差分ビルドの高速化ぐらいしか分けるモチベーションがありませんでしたからね。

StackでもCabalと同じく-jsemを実装しようとする動きはあるようです。 Support GHC's -jsem <sem> initiative · Issue #6131 · commercialhaskell/stack

これが実装されればstack installをglobalな環境向けに実行するときも、高速にビルドさせるのが容易になりそうです。これまでもモジュール単位では並列ビルドしてくれましたが、どうしてもpandocなどがモジュールたくさんで遅いという状態になっていたので今後の進展に期待です。他にもGTKなどのバインディングも大量の並列したモジュールが含まれるため期待できます。