組み込みエンジニアの方は、C/C++でファームを作っていることが多いかと思います。最近はマイコンも多機能になってきて、ビルド時間もバカにならなくなってきています。今回は、C/C++のコンパイルを爆速にするccacheというツールをご紹介します。gccやg++だけでなくARM社純正開発ツール RVCT, RVDSの armccにも使えます。
ビルド時間は短いほうが正義!
開発途中は単純化すると「コード修正」→「単体テスト」→「デバッグ」のサイクルの繰り返しとなるかと思います。このサイクルにはビルドという作業が必要です。もし仮にビルドに30分かかるとしたら、1営業日に最大16回しかこのサイクルを回せんません(8時間/1営業日と仮定)。16回でバグを全部なくせ、というのは結構しんどいですよね。もしこのビルド時間が10倍速くになればどうでしょうか。最大で160回もサイクルを回せます。前者と後者とでどちらが生産性が高いかは比べるまでもないですよね。
これはものすごく単純化した例ですが基本的な考えは一般化できます。みなさん、日々どのくらいビルドしていますか?ちょっとビルドが早くなるだけで相当の時間を稼げるのではないでしょうか。このビルド高速化を実現するツールが今回紹介するccacheです。
ccacheとはなんですか?
ccache とは C/C++のコンパイル速度を高速化するツールです。オープンソースであり、LinuxはもちろんWindowsでも動作します。また、導入方法も簡単です。例えば、Androidのccacheを利用したビルドの効果測定が京都マイクロコンピュータ社のブログに載っています。また、
IBM社の開発者向け記事にもccacheの効果が載っています。
コンパイル速度にはHDD/SSDのアクセス速度やコンパイラライセンスの取得時間も関わるため一概には言えませんが、個人の経験では悪くても2倍は余裕で早くなるかともいます。環境との相性がよければ目を疑うほどの爆速になります。
ccache の仕組みは?
make の原理のおさらい
ビルドを効率化するためのツールとして、一番基本的なツールは Makefile ですよね。もし makeを使わなければ、ビルドするために毎回すべてのソースコードをコンパイルしなければなりません。一方makeは、ソースコード(.c/.cpp、インクルードしている.h/.hpp)の最終修正時刻とそれから生成される.oファイルの生成時刻を比較して、ソースコードの修正時刻の方が新しい場合のみソースコードをコンパイルします。つまり修正したソースコードだけをコンパイルすることで全体のビルド時間を短縮します。
ccache の原理の概要
ccache はそこからさらに高速化をすすめます。ソースコードをコンパイルする時、コンパイラはまずマクロの展開などのプリプロセッサ処理を行います。次に、そのプリプロセス済みのコードをコンパイルしてオブジェクトファイル(.o)に変換します。
ccacheは、その「プリプロセス済みのコード」と「コンパイルずみのオブジェクトファイル(.o)」の2種類のファイルをキャッシングします。ここでいうキャッシングとはCPUのキャッシュメモリとは関係ありません。広義のキャッシュのことです。具体的にいうと、ビルド時に生成したそれら中間ファイルをPCのHDD/SSDに保存します。そして次回以降、その保存した中間ファイルが使えそうだったらそれを利用します。これにより実際にコンパイルする回数が減り飛躍的にビルド時間を短縮します。
ものすごく単純な例でいうと、make → make clean → 再度 make とした場合、1度目のmakeと2度目にmakeのビルド時間はほぼ同じになるはずです(I/Oバッファの分だけ2回目の方が早いかもですが)。一方、ccache を使っている場合は、1度目より2度目の方が何倍も早くなります。1度目のmake時にキャッシングしたデータを流用してコピーするだけでコンパイルはしないのであっという間に終わります。びっくりするくらい早くなります。
どのようにしてccacheが再コンパイルが必要かどうか判断しているかは、ソースコードファイルのハッシュ値を使っているようです。興味のある方はccacheのソースコードを見てみてください。コード量はそれほど多くないので、頑張れば読めるかなと思います。
また、コンパイラを実行する回数が減る副次的な効果として、ライセンスの消費量が減ります。例えば armcc などの有償のコンパイラをフローティングライセンスで使用している場合、開発チームが一度に使えるライセンス数には上限があります。例えばライセンス数の上限が50で、100人の開発者が同時にビルドを始めると、ライセンスが枯渇してしまいライセンスがあくまで待たされたり、最悪ビルドに失敗したりしてしまう可能性が高くなります。しかしccacheを使えば、キャッシングしたファイルが流用できる場合はコンパイラを実行せずに単にそのファイルをコピーするだけなのでライセンスを使用しません。よって使用するライセンス数を抑えることができます。
少し話は逸れますが、ARMのライセンス消費の推移をグラフ化する方法を下記の記事に書いています。バージョンによっては使えないかもしれませんが、ご参考までに。
https://hangstuck.com/arm-lmstat-graph/
ccacheのインストール
Linuxの場合はパッケージマネージャでサクッとインストールできるかと思います。もしできなければソースコードからビルドすればOKです。 ソースコードからビルドできない環境のWindowsの場合は私の方でビルドしたバイナリも用意しています。それぞれの方法は下記を参照してください。
Linux でのインストール
大抵のディストリビューションにて、パッケージマネージャからインストールできると思います。下記のような感じでしょうかね。お使いの環境に合わせてパッケージマネージャでインストールしてください。
sudo apt-get ccache
sudo pacman -S ccache
Mac OS X でのインストール
homebrew でのインストールが一番楽でしょう。homebrewを導入した上で、下記のように実行するだけです。
brew install ccache
MSYS2@Windows でのインストール
WindowsでかつMSYS2(やGit for Windows)をお使いの方もパッケージマネージャからインストールできます。
pacman -S ccache
パッケージマネージャが使えない場合@Linux
仕方ないので自分でソースコードからインストールしましょう。と言っても簡単ですが。下記のようにすればOKです。最初にソースコードを落としてくるところは、バージョンによってファイル名が違うので、その時の最新版を持ってきてください。
$ wget https://www.samba.org/ftp/ccache/ccache-3.4.2.tar.gz $ tar zxvf ccache-3.4.2.tar.gz $ cd ccache-3.4.2/ $ ./configure $ make $ make install
WindowsでMSYS2が使えない場合
職場によってはMSYS2が使えない状況もありえるかと思います。そこで、MINGW64にてビルドしたバイナリ(ccache.exe)を用意してみました。
下記のGitHubリポジトリに置いていますので、ダウンロードしてお使いください。
ダウンロードしたccahce.exeとzlib1.dllを任意のフォルダに置いて、そこにPATHを通せばOKです。
ccacheを使う
ccacheが動作するようにしましょう。gccなどのコンパイラコマンドの前にccaheを追加するだけです。つまり、たとえば
gcc hoge.c -c -o hoge.o
としてコンパイルしていたなら
ccache gcc hoge.c -c -o hoge.o
とするだけです。
ccacheが動作しているか確認するには、キャッシュのヒット情報をみてみればわかります。-sオプションでこれまでのキャッシュヒット率などの統計情報が表示されます。一度めはcache miss が増えると思いますが、2度目以降はcache hitのところがどんどん増えていくはずです。
$ ccache -s cache directory /Users/username/.ccache primary config /Users/username/.ccache/ccache.conf secondary config (readonly) /usr/local/Cellar/ccache/3.4.2/etc/ccache.conf stats zero time Sat Apr 14 00:50:12 2018 cache hit (direct) 32 cache hit (preprocessed) 0 cache miss 16 cache hit rate 66.67 % cleanups performed 0 files in cache 51 cache size 274.4 kB max cache size 5.0 GB
ccacheを使うためのMakefileの変更例
Makefile を使っている場合は、下記のようなパターンルールがどこかに記載されているはずです。
%.o : %.c $(CC) $(CFLAGS) -c $< -o $*.o
これを下記のようにしてあげればOKですね。.cpp でも要領は同じです。
%.o : %.c ccache $(CC) $(CFLAGS) -c $< -o $*.o
これじゃちょっとわかりづらいかもしれないので、Makefileの具体例を下記のところに置いています。ccache が使えればccache を有効に、cchacheがインストールされていなければ普通にgccを使うようにしています。
set_toolchain.mk
define_pattern_rule.mk
まとめ
今回はC/C++のコンパイルを高速化するツールccacheについてご紹介しました。Windows、Linux、Mac OS Xそれぞれでのインストール方法と、Makefileの修正方法を説明しました。これでビルド時間が数倍になります!
ccacheのオプションの詳細はccache公式ドキュメントをご参照ください。
コメント
ccache 入れましたが、file not found エラーでビルド全体が失敗しました。
cache取得でエラーがあれば普通にコンパイルする。という仕様ではなく、
cache取得でエラーがあればビルド全体を失敗する。という仕様になっているようです。
それだと初回のコンパイルはかならず失敗することになり、さらに初回がだめなら2回目以降も失敗することになりませんか。永遠に失敗し続けるかと。
仕様ではないので、そのような現象がおこるならフォーラムなどに投げてみてはいかがでしょう?