torutkのブログ

ソフトウェア・エンジニアのブログ

distccによる分散コンパイルでビルド時間を短縮

背景

C++は、コード規模の増分に対して指数的にコンパイル・リンク時間が増大します。
以前、C++Javaのビルド時間比較で調査したデータid:torutk:20071104と、id:torutk:20071107から、C++のコード規模によるビルド時間の違いをまとめ直したのが以下の表です。

対象プログラム名 総行数 命令行数 ファイル数 ビルド時間
ACE 5.6.1 307,819 79,930 1,248 00:10:40
ACE+TAO 5.6.1 1,066,708 247,391 2,592 2:35:30

コード規模が3倍でビルド時間は15倍となっています。

ビルド時間を短縮するアプローチ

ビルド時間を早めるための工夫がいくつか存在しています。

  1. キャッシュ等前回実行した結果を活用して処理時間を短くする
    1. コンパイル済みのオブジェクトはソースが変更されていなければ再コンパイルしない
    2. ヘッダーファイルを事前コンパイルしてプリプロセス時間を短縮する
    3. ヘッダーファイルの解析情報をメモリにキャッシュしてプリプロセス時間を短縮する
  2. 並列処理によって処理時間を短くする
    1. 1つの計算機上で複数CPUを使い同時に複数のコンパイルを実行する
    2. 複数の計算機上で複数CPUを使い同時に複数のコンパイルを実行する

開発者が自分の作業で何回もビルドを行う場合、キャッシュによる時間短縮は効果的です。しかし、ゼロから全てをビルドする(クリーンビルド)場合、キャッシュ的アプローチによる時間短縮は期待できません。そこで、並列処理によって全体のビルド時間を短縮します。

最近の計算機環境は、マルチコアによる並列処理の可能性、LAN上の複数計算機による並列処理の可能性が高まっているので、並列・分散による短縮が期待できます。

Linux/GCC環境

前回実行した結果を利用するアプローチでは、1.GNUmake、2.GCCのPCH、3.ccacheといった仕組みが利用可能です。

並列処理によるアプローチでは、1.GNUmake、2.distccと言った仕組みが利用可能です。

distccを使う

同じバージョンのGCCが入ったマシンがネットワーク上に複数ある場合、distccを使って分散ビルドを実行する環境が構築できます。

インストール

distccは、現在http://distcc.org/ の実体であるhttp://code.google.com/p/distcc/で公開されています。
Downloadsでは、ソースコードのtarballの他、i386系プロセッサ用のバイナリがRPMパッケージ形式およびdebianパッケージ形式で提供されています。本日現在このページにて提供されるバージョンは3.1で、2008年12月に載せられたものです。
また、Sourceでは、Subversionリポジトリからdistccの最新コードを取得する方法が載っています。

CentOS 5.3でのインストールの問題
  • distcc-serverのRPMパッケージをインストールすると、distccサービスの起動に失敗する。
    • /etc/distcc/clients.allowに有効な設定を記述する。RPMパッケージをインストールした直後はコメントアウトされ、有効な定義が1つもないので、定義を記述します。例として、自ホストと、自ホストと同一LAN(仮にネットワークアドレスが192.168.1.0とした場合)からのアクセスを許可する設定を以下に示します。
127.0.0.1
192.168.1.0/24
  • IPv6で接続しようとする

IPv6を有効にしていると、distccdがIPv6で待ち受けるようです。/var/log/messagesを見ると、接続してくるクライアントアドレスが、::ffff:192.168.1.10 のようになっています。動作に支障がない環境であればそのままでかまわないと思います。

    • distccdの--listenオプションでIPv4アドレスをバインドする
$ distccd --daemon --listen=192.168.1.1 --allow=192.168.1.0/24
  • IPv6で接続してきたクライアントの許可

clients.allowではIPv4で指定しても、接続がIPv6だと許可したクライアントと見なされないかもしれません。

    • /etc/distcc/clients.allowにIPv4射影アドレスを指定する
::ffff:192.168.1.0/120
distccサービスの稼動

distcc-serverパッケージを入れると、/etc/rc.d/init.d/distccが置かれ、chkconfig管理下になりますが、ランレベルは全てoff状態です。

# chkconfig distcc on
# service distcc start
distccの利用(ノーマルモード

プリプロセス処理が完了したソースファイルをdistccサーバに転送してコンパイル結果を受け取ります。

環境変数DISTCC_HOSTSに、distccサーバ(複数)を指定します。標準的なMakefileが用意されていれば、makeの-jオプションで並列度を指定し、コンパイラにg++の代わりにdistccを指定すればよいでしょう。慣例では、C++コンパイラのコマンド名は変数CXXで指定します。

$ export DISTCC_HOSTS='localhost red blue yellow pink green'
$ make -j12 CXX=distcc
   :
distccの利用(pumpモード)

プリプロセス処理からdistccサーバに依頼します。ソースファイルとそのソースファイルがインクルードするヘッダーファイル群をdistccサーバに転送してコンパイル結果を受け取ります。

環境変数DISTCC_POTENTIAL_HOSTSに、distccサーバ(複数)を指定します。ノーマルモード同様標準的なMakefileが用意されていれば、pumpコマンドのオプションに、-jオプションとC++コンパイラのコマンドをdistccに指定したmakeコマンドを記述します。

$ export DISTCC_POTENTIAL_HOSTS='localhost red blue yellow pink green'
$ pump make -j12 CXX=distcc
    :
分散コンパイル状況の把握
  • クライアント側

distccmon-textコマンドで、その時点でdistccサーバ名と依頼しているファイルを表示します。

  • サーバ側

/var/log/messagesに、distccdがネットワークから受け取ったログが逐次追加されています。

distccの最新ソースとRPMパッケージのビルド

distccの最新ソースはsubversionリポジトリで管理され、取得可能です。ダウンロードで用意されているVer.3.1は、2008.12の時点のものなので、それ以降の修正が必要ならば、ソースをsubversionリポジトリから取得し、ビルドします。

RPMパッケージ作成

distccのソースツリーの中に、packaging/RedHat/rpm.specがあります。specファイルからビルドするには、専用スクリプトpackaging/rpm.sh も提供されています。

  • rpmbuildでエラー

%filesに、以下を追加します。rpmbuild時に警告が出ますがこれでしのぎます。

%{_libdir}/python2.4/site-packages/include_server
  • サービス起動エラー

clients.allowにとりあえず127.0.0.1だけ記述すればインストール時にサービスが起動するようになります。rpmbuildのパッチファイルとして用意するのがよいでしょう。

  • updateの考慮不足

distccのspecファイルには、RPMパッケージのアップデートに対する対応エラーがあります。

参考リンク

distccの簡単な使い方、pumpモードの使用方法、ccacheとpumpモードのトレードオフについて検討しているページ。