ｃｃａｃｈｅでＦｒｅｅＢＳＤをｂｕｉｌｄｗｏｒｌｄした場合の効率について

# はじめに

今更ではあるがｃｃａｃｈｅの話である。ビルドする際の高速化を担うツールとしては有名であるが、調べてみると、古い情報しか見当たらない。
最近のＦｒｅｅＢＳＤでは指定方法が変わった、といったような情報が見当たらないので、今回、その点をメインに効果の程を解説する。

※従来と設定方法が変わって、極めて簡略に制御できるようになってます。

## 検証環境

- FreeBSD 13.0-RELEASE-p5
- FreeBSD 12.2-RELEASE-p11

ｃｃａｃｈｅのインストール手順そのものは今回は取り上げない（特別な手順は無い）。ただしディスク使用量調査のため、事前に下記のコマンドを実行して、キャッシュが効かなくなる状況を発生しないようにした。
結論から言えば、（ＦｒｅｅＢＳＤのビルドだけなら）デフォルト使用量（５ＧＢ）でも問題無い。

```shell:キャッシュ容量の拡張（５ＧＢ→３２ＧＢ）
ccache -M 32G
```

なお「バイト」のＳＩ接頭辞は$１０^３$を基本としているため、$２^{１０}$にしたい場合は `32Gi` のような形で明示する必要がある（それでも表示は変更できない）。
また、数字だけの指定で単位を指定しない場合 `G` として解釈される。

## 設定（/etc/src.conf）

```makefile:/etc/src.conf
WITH_CCACHE_BUILD= yes
```

ｃｃａｃｈｅを使用して `buildworld` するだけなら、上記設定を行えばよい。環境変数 `CC` に `CC="ccache cc"` と設定するようなことはしなくてもよくなっている（ココ重要！）。

`buildworld` あるいは `buildkernel` 時に必要あるいはコンパイル・インストール対象となるコンポーネントのオンオフについては [src.conf(5)](https://man.freebsd.org/src.conf/) を参照すれば、一通り載っている、が、自分はまだ見逃しを確認していない（量多過ぎ…）。

# make buildworld の実施

キャッシュが無い状態からの `make buildworld` の実行を行った。キャッシュのヒット率等の情報を精査するために、`buildworld` 終了後に統計情報（`ccache -s` コマンドの実行）を取得している。

## 一周目の実行結果

下記の通り、ほぼ全てのファイルのコンパイルが実施された。この時、ＣＣＡＣＨＥによるオーバーヘッドが発生することから、通常のコンパイル時間より長くなる。
また若干の再コンパイルが行われたが、高々０．２％も無いので無視していい。

```console:一周目の実行結果（キャッシュ無し状態からのビルド）
cache directory                     /root/.ccache
primary config                      /root/.ccache/ccache.conf
secondary config      (readonly)    /usr/local/etc/ccache.conf
cache hit (direct)                     5
cache hit (preprocessed)              77
cache miss                         46123
cache hit rate                      0.18 %
called for link                      116
ccache internal error                  1
unsupported code directive             3
no input file                          1
cleanups performed                     0
files in cache                    137311
cache size                           2.7 MB
max cache size                      32.0 GB
```

また、実際のキャッシュ使用量が反映されないなど、一部参考にならない結果が得られる。これについては後述する。

## 二周目の実行結果

上記データ収集後、再度 `buildworld` を実施した。下記の通りの結果が得られた。後述するが、キャッシュに１００％ヒットした結果として「５０．０９％」の結果が得られている。

```console:二週目の実行結果（キャッシュされた状態からのビルド）
cache directory                     /root/.ccache
primary config                      /root/.ccache/ccache.conf
secondary config      (readonly)    /usr/local/etc/ccache.conf
cache hit (direct)                 45050
cache hit (preprocessed)            1237
cache miss                         46123
cache hit rate                     50.09 %
called for link                      232
ccache internal error                  2
unsupported code directive             6
no input file                          2
cleanups performed                     0
files in cache                    137454
cache size                           2.7 MB
max cache size                      32.0 GB
```

## 統計情報をゼロクリアしての三周目の実行結果

`ccache -z` を実行して、再度 `buildworld` を実施した。下記の通りの結果が得られる。二周目実行時にはわからなかった、「１００％ヒット」が、確認できる。この時、一切コンパイルが行われていないため、`buildworld` の実行時間が短縮されている。

```console:統計情報のゼロクリア後の三周目の実行結果（キャッシュされた状態からのビルド）
cache directory                     /root/.ccache
primary config                      /root/.ccache/ccache.conf
secondary config      (readonly)    /usr/local/etc/ccache.conf
cache hit (direct)                 45186
cache hit (preprocessed)            1019
cache miss                             0
cache hit rate                    100.00 %
called for link                      116
ccache internal error                  1
unsupported code directive             3
no input file                          1
cleanups performed                     0
files in cache                    137578
cache size                           2.7 MB
max cache size                      32.0 GB
```

## クリーンナップしてみる

キャッシュサイズが反映されない件について `ccache -c` を実行して、統計データに反映させることができる。

```console:クリーンナップ後の統計情報の正常化された状態
cache directory                     /root/.ccache
primary config                      /root/.ccache/ccache.conf
secondary config      (readonly)    /usr/local/etc/ccache.conf
cache hit (direct)                 45186
cache hit (preprocessed)            1019
cache miss                             0
cache hit rate                    100.00 %
called for link                      116
ccache internal error                  1
unsupported code directive             3
no input file                          1
cleanups performed                     0
files in cache                    137578
cache size                           4.5 GB
max cache size                      32.0 GB
```

ご覧のように５ＧＢでもギリギリ間に合うことが確認できる。

# ベンチマーク

## buildkernel の事例

[ｋａｒｌ](https://twitter.com/karl0204)さんから、下記の環境での `make buildkernel` での計測結果をいただいたので表にまとめてみた。

* ＣＰＵ： AMD Ryzen9 3900 （3.10GHz／4.30GHz）
* メモリ： 32GB
* ストレージ： WesternDigital SN550（ＮＶＭｅ接続）・ＺＦＳ運用
* ビルド対象： FreeBSD 14-CURRENT

 ビルド手順             |  ＣＣＡＣＨＥ無し時間 | ＣＣＡＣＨＥ有り時間
:---------------------- | --------------------: | -------------------:
`make      buildkernel` |            １４０６秒 |            ８３７秒
`make -j4  buildkernel` |              ３６６秒 |   -
`make -j8  buildkernel` |              ２００秒 |   -
`make -j12 buildkernel` |              １５５秒 |              ８２秒
`make -j16 buildkernel` |              １３６秒 |   -
`make -j20 buildkernel` |              １２３秒 |   -
`make -j24 buildkernel` |              １１５秒 |              ５５秒

なお全ＣＰＵリソースを消費し切れなかったとのことでした。

## buildworld の事例

* ＣＰＵ： Intel Pentium N4200 （1.10GHz／2.50GHz・Apollo Lake／Goldmont アーキテクチャ）
* メモリ： 16GB
* ストレージ： Transcend MTS400S（ＳＡＴＡ接続）・ＺＦＳ運用
* ビルド対象： 12.2-RELEASE-p11

ビルド手順※               | ビルド時間
:------------------------- | --------------------:
ＣＣＡＣＨＥ無し           |        ２２３１７秒
ＣＣＡＣＨＥ有り（一周目） |        ２５６０３秒
ＣＣＡＣＨＥ有り（二周目） |          ３８４１秒
ＣＣＡＣＨＥ有り（三周目） |          ３８５４秒

※いずれも `make -j5 buildworld` を指定

# まとめ

- ビルド並列度（`-j` オプションの指定）の高低にかかわらず、１００％キャッシュ時には数倍レベルの時間短縮効果（数分の一の時間でビルドが終る）が得られる。
- その効果もほぼＣ言語だけで構成された `buildkernel` の場合で２倍程度、ＬＬＶＭのような重たいビルドを含む `buildworld` の場合は６倍程度の効果が確認できる。
- ｃｃａｃｈｅを導入した直後のオーバーヘッドは１４％発生するが、二回目以降の効果を得るための代償としては誤差である。
- 少し修正しては全体をビルドしたいようなケース（開発等）での効果は非常に大きい。
- また今回、純粋に再ビルドするだけなら、１００％コンパイルしないことも示した。これは `make clean` でオブジェクトが消えた状態でも確認している。
- 最近のＦｒｅｅＢＳＤでは、開発環境（ＬＬＶＭ）の構築に非常に時間がかかるようになったが、これが極めて短縮（時間的にもメモリ的にも）できる効果は大きい。
- とは言え、初回コンパイル時にメモリが必要となる点は変わらないので、少メモリ環境でのビルドは相変わらずつらいことには変わりない。

## ｃｃａｃｈｅのの代表的なコマンド・オプション

ｃｃａｃｈｅ自体シンプルなせいか、あまりこの点について記載されているのを見ないため、簡単にまとめて見た。

コマンド       | 意味                                     | 備考
-------------- | ---------------------------------------- | ---------------------------------------------------------------------------
`ccache -M ｎ` | キャッシュ容量の設定（無指定の場合`Ｇ`） | 最大キャッシュ容量を指定する※
`ccache -s`    | 統計情報の表示                           |
`ccache -z`    | 統計情報のクリア                         |
`ccache -c`    | キャッシュディレクトリの精査             | クリーンナップ処理（キャッシュされたデータは削除しない）
`ccache -C`    | キャッシュの削除                         |

※`ｎ`に指定可能なサフィックスとしては「`k`」「`M`」「`G`」「`T`」「`Ki`」「`Mi`」「`Gi`」「`Ti`」が指定可能である。

# よくある質問とその答え

## Ｑ．/etc/make.conf に設定しちゃ駄目ですか？

Ａ．/etc/make.conf は必ず読まれるため、事実上 /etc/src.conf で設定したことと同じになります。その代わり、ＦｒｅｅＢＳＤのビルド以外で設定が参照された場合、その振る舞いは保証されません。
その辺り気にしないのであれば設定しても問題ありません。同じ `make` コマンドを参照する他のビルドと区別するために /etc/src.conf に設定するようにしましょう。