「ccache1がUnityのiOSビルド2でも利用できるのか?」「また、出来るとした場合にはどれくらい効果があるのか?」について調べてみました。
私自身、ccache
の導入そのものが初だったこともあるので、前半は主に**「ccache
のインストール方法や設定項目」などについて記載していき、後半から「Unity上での導入手順及び効果」**について記載していければと思います。
(予めccache
を知っている方は前半部分読み飛ばしても良いかも)
もし間違いや不足などあればコメントにて教えて頂けると幸いです!
※まだWIP
書いておいてなんですが...後半の「Unity iOSビルドで導入した際の効果」についてはまだ未調査なところがチラホラと有り、完全に調べきれているとは言い切れないところもあるので、一先ずは**「雰囲気で導入してみた際の途中経過までのメモ書き」**的な感じで読んで頂ければと思います。。
(未調査項目は必要に駆られたタイミングで調査を再開し、随時追記していく予定..)
TL;DR
先にUnity iOSビルドで導入した際の効果について、今の時点での検証結果から纏めます。
(導入関連は特に纏めることが無いので割愛)
※Unity iOSビルドで出力される一式を記事中ではxcodeproj
と表記してます。以降の解説でも同様の表記となります。
- Unityから出力した
xcodeproj
にて、一度ビルドした後にCleanしてからリビルドしたらキャッシュヒットしてビルド時間が短くなった -
「Build」と「Archive」は別のキャッシュが作られる
- → 「Build」後 に「Archive」を行っても後者のビルド時間は短くならない
- Unity iOSビルド時に「Append」で
xcodeproj
を更新してもキャッシュヒットは維持される -
Unity iOSビルド時に「Replace」で
xcodeproj
を置き換えたらキャッシュヒットしなくなった- ※「既存のxcodeprojを手動で削除 → ビルド」も同様
結果だけで見ると「Appendでの更新」や「一度ビルドした後のClean/Build」ではキャッシュヒットして高速化出来るものの、Replace相当の手順でxcodeproj
そのものを置き換えてしまうとキャッシュヒットしなくなるように思われました。
とはいえまだ雰囲気で使っているところはある上に、原因については詳細まで追いきれていないので、気が向いたら調査を再開していきたいところ...。
→ 幾つかの未調査項目などはこちらに纏めている。
ccacheの導入
macOS上でのccacheの導入方法や各種設定項目についてメモしていきます。
(導入時のOSはmacOS Catalina 10.15.3
)
インストール
brew
コマンドからインストールできます。
$ brew install ccache
ccacheの設定について
幾つか必要そうな情報だけメモします。
詳細はccache -h
でヘルプを参照してください。
設定の確認
ccache -p
を叩くことで現在の構成を確認できます。
各項目の概要についてはドキュメントにある「Configuration settings」の章をご確認ください。
`ccache -p`の出力結果(クリックで展開)
出力結果のプレフィックスに(default)
と付いている物はデフォルト設定であり、(/Users/<user>/.ccache/ccache.conf)
と付いているものはユーザーが任意で設定変更した項目となります。
※以下で言うとmax_size = 5.0G
が変更されている。
# -p, --show-config show current configuration options in
# human-readable format
$ ccache -p
(default) base_dir =
(default) cache_dir = /Users/<user>/.ccache
(default) cache_dir_levels = 2
(default) compiler =
(default) compiler_check = mtime
(default) compression = false
(default) compression_level = 6
(default) cpp_extension =
(default) debug = false
(default) depend_mode = false
(default) direct_mode = true
(default) disable = false
(default) extra_files_to_hash =
(default) hard_link = false
(default) hash_dir = true
(default) ignore_headers_in_manifest =
(default) keep_comments_cpp = false
(default) limit_multiple = 0.8
(default) log_file =
(default) max_files = 0
(/Users/<user>/.ccache/ccache.conf) max_size = 5.0G
(default) path =
(default) pch_external_checksum = false
(default) prefix_command =
(default) prefix_command_cpp =
(default) read_only = false
(default) read_only_direct = false
(default) recache = false
(default) run_second_cpp = true
(default) sloppiness =
(default) stats = true
(default) temporary_dir =
(default) umask =
キャッシュサイズの変更
キャッシュサイズはccache -M
から変更できます。
単位はG
、M
など付ければ指定可能です。
# -M, --max-size=SIZE set maximum size of cache to SIZE (use 0 for no
# limit); available suffixes: k, M, G, T (decimal)
# and Ki, Mi, Gi, Ti (binary); default suffix: G
$ ccache -M 1G
Set cache size limit to 1.0 GB
※設定ファイルから変更する場合
コマンドなどで設定した情報はccache.conf
と言うファイルに保存されます。
こちらは幾つか存在しますが、今回は以下のパスにある設定ファイルに変更を加えてます。(詳細は「ドキュメント -> Configuration」を参照)
/Users/<user>/.ccache/ccache.conf
こちらを直接編集することでも設定変更が可能です。
max_size = 1G
※環境変数から設定を変更する場合
設定ファイル以外にも環境変数からも設定を変更することが出来ます。
環境変数名は「ドキュメント -> Configuration settings」にある項目名横のカッコ内に記載されてます。(e.g. max_size (CCACHE_MAXSIZE)
)
# 最大キャッシュサイズの変更
$ export CCACHE_MAXSIZE=5G
キャッシュディレクトリの変更
キャッシュディレクトリの変更は以下のコマンドから変更できます。(外部ストレージを指定する例)
こちらもccache.conf
を書き換えたり、環境変数から指定して変更することも可能です。
# -o, --set-config=K=V set configuration item K to value V
$ ccache -o cache_dir=/Volumes/<外部ストレージ>/ccache
ccache.conf
の書き換え
max_size = 5G
cache_dir = /Volumes/<外部ストレージ>/ccache
環境変数から指定
# 最大キャッシュサイズの変更
$ export CCACHE_DIR=/Volumes/<外部ストレージ>/ccache
※ -o
コマンドについて
-o
コマンドはKey(設定項目名)
とValue(設定値)
を指定することで変更を適用できるコマンドです。
(イメージ的にはコマンドからccache.conf
を書き換えることが出来る機能)
ただし、中にはccache -M
の様にデフォルトで設定変更がコマンド化されているものもあります。
// これら2つは同じ機能
$ ccache -o max_size=1G
$ ccache -M 1G
キャッシュデータについて
統計情報の確認
ccache -s
を叩くとキャッシュの統計情報を確認することができます。
各項目の概要についてはドキュメントにある「Cache statistics」の章をご確認ください。
# -s, --show-stats show summary of configuration and statistics
# counters in human-readable format
$ ccache -s
cache directory /Users/<user>/.ccache
primary config /Users/<user>/.ccache/ccache.conf
secondary config (readonly) /usr/local/Cellar/ccache/3.7.9/etc/ccache.conf
cache hit (direct) 0
cache hit (preprocessed) 0
cache miss 0
cache hit rate 0.00 %
cleanups performed 0
files in cache 0
cache size 0.0 kB
max cache size 5.0 GB
キャッシュの削除
キャッシュはccache -C
で削除できます。
# -C, --clear clear the cache completely (except configuration)
$ ccache -C
Cleared cache
※ちなみに、大文字の-C
と小文字の-c
は別コマンドになっているので注意。
# -C, --clear clear the cache completely (except configuration)
$ ccache -C
# -c, --cleanup delete old files and recalculate size counters
# (normally not needed as this is done
# automatically)
$ ccache -c
統計情報のクリア
ccache -s
で出力できる統計情報はccache -z
を叩くことで初期化する事が出来ます。
# -z, --zero-stats zero statistics counters
$ ccache -z
Statistics zeroed
UnityのiOSビルドで導入してみた
ここからはUnityのiOSビルドにて導入する際の手順や結果などについて纏めていきます。
導入手順
導入手順については以下の記事を参考にさせていただきました。
順に解説していきます。
ちなみに、iOSビルドでは主にUnityから出力されたxcodeproj
に対してccache
を適用し、Xcodeビルドの高速化を行っていく形となります。
→ 逆に言うとUnity側のプロセス(xcodeproj
の出力など)自体はccache
の適用外 と言う認識
Xcode側でccacheを呼び出すためのラッパースクリプトを用意
xctoolchain
内にccacheを呼び出すためのラッパースクリプトを用意します。
参考記事に倣ってshell内で完結させてますが、予めccache_wrapper
を別途生成して該当パスに保存 → パーミッションの変更で用意することも可能です。
$ sudo vi /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ccache_wrapper
----------------------------------
#!/bin/bash
which ccache
if [ "$?" -eq 0 ]; then
# ※ `CCACHE_SLOPPINESS(sloppiness)`はccache.confで設定することも可能
export CCACHE_SLOPPINESS=pch_defines,time_macros,clang_index_store
exec `which ccache` /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -Qunused-arguments "$@"
else
exec /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -Qunused-arguments "$@"
fi
----------------------------------
$ sudo chmod 755 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ccache_wrapper
Editor拡張でxcodeproj
に対してラッパースクリプトを自動設定
後はUnity側で以下のEditor拡張を用意します。
こちらを導入することで、iOSビルド時に必要なUser-Defined
等が自動で設定されます。
(上記で用意したccache_wrapper
の設定など)
#if UNITY_IOS
using System.IO;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;
namespace Samples
{
static class CcacheSettings
{
/// <summary>
/// xcodeprojにccache向けの設定を適用するサンプル
/// </summary>
[PostProcessBuild]
public static void OnPostprocessBuild(BuildTarget buildTarget, string path)
{
if (buildTarget == BuildTarget.iOS)
{
var pbxProjectPath = PBXProject.GetPBXProjectPath(path);
var pbxProject = new PBXProject();
pbxProject.ReadFromString(File.ReadAllText(pbxProjectPath));
var target = pbxProject.ProjectGuid();
pbxProject.AddBuildProperty(target, "CC", "$(DT_TOOLCHAIN_DIR)/usr/bin/ccache_wrapper");
File.WriteAllText(pbxProjectPath, pbxProject.WriteToString());
}
}
}
}
#endif
ビルド時間の計測
ビルドに用いる検証用プロジェクトには、以前書いたこちらの記事にあるゲームプロジェクトを利用します。
(実案件レベルとは行かずとも、コンパイル時間を検証するにあたって多少のコード量は欲しかったので)
環境/スペック
- macOS Catalina 10.15.3
- MacBook Pro (15-inch, 2018)
- CPU: 2.2 GHz 6コア Intel Core i7
- メモリ 16GB 2400 MHz DDR4
- MacBook Pro (15-inch, 2018)
- Unity2019.3.11f1
- Xcode 11.4.1
検証内容
以下に検証項目と期待する動作を定義します。
こちらをccache -zC
でキャッシュ及び統計情報を消した状態で上から順にビルドしていき、ビルド時間やキャッシュヒット率などの変動を見ていきます。
-
①: Unityから出力された
xcodeproj
で「Build → Clean」を3回繰り返す- → そもそも
ccache
は機能するのか?
- → そもそも
-
②: ①から継続して「Clean → Archive」を行う
- → ①で得たキャッシュはアーカイブ時にもヒットするのか?
-
③: ①, ②でビルドした
xcodeproj
に対し、Unity側でAppendビルド- → Unity側でAppendした際にキャッシュは維持されるのか?
-
④: ③の
xcodeproj
に対し、Unity側でReplaceビルド- →
xcodeproj
を丸々置き換えた際にもキャッシュは維持されるのか?
- →
ちなみに今回は「そもそもとして機能するのか?」に注目しているので、(検証としては微妙かもしれませんが...)ビルド毎にコード変更などは加えてません。
記載しているビルド時間はそれを踏まえた結果として見てください。
ビルド設定
デフォルトから変更した項目のみ記載します。
各項目の設定についてはドキュメントをご覧ください。
compiler_check = content
sloppiness = pch_defines,time_macros,clang_index_store
①の結果
2回目からキャッシュヒット率が上がっており、ビルド時間が大幅に短くなっていることが確認できました。
この事からccache
自体は正しく機能しているみたいです。
ccache -s |
1回目 | 2回目 | 3回目 |
---|---|---|---|
ビルド時間(秒) | 202.0sec | 25.4sec | 24.1sec |
cache hit (direct) | 0 | 404 | 808 |
cache hit (preprocessed) | 0 | 5 | 5 |
cache miss | 409 | 409 | 414 |
cache hit rate | 0.00 % | 50.00 % | 66.26 % |
cleanups performed | 0 | 0 | 0 |
files in cache | 1913 | 1913 | 1937 |
cache size | 583.7 MB | 583.7 MB | 584.2 MB |
max cache size | 5.0 GB | 5.0 GB | 5.0 GB |
②の結果
①の状態から継続してアーカイブを行ってますが、1回目の結果を見る分にはキャッシュヒットが上昇していない上にキャッシュミスが目立っているので、恐らくは①のキャッシュが参照されていないように思われました。 (キャッシュミスの理由についてはまだ未調査...)
2回目からは新たに生成されたキャッシュを見に行っているためか、再びヒット率が上昇してビルド時間が短くなってます。
ccache -s |
1回目 | 2回目 | 3回目 |
---|---|---|---|
ビルド時間(秒) | 308.7sec | 140.6sec | 132.5sec |
cache hit (direct) | 808 (prev:808) | 1211 | 1620 |
cache hit (preprocessed) | 5 | 5 | 5 |
cache miss | 823 (prev:414) | 829 | 829 |
cache hit rate | 49.69 % | 59.46 % | 66.22 % |
cleanups performed | 0 | 0 | 0 |
files in cache | 3849 | 3877 | 3877 |
cache size | 1.8 GB | 1.8 GB | 1.8 GB |
max cache size | 5.0 GB | 5.0 GB | 5.0 GB |
③の結果
Unity上からAppendしてxcodeproj
を更新した際の結果です。
ビルド時間やキャッシュヒット/ミスの数値を見た感じだと、①と②で生成されたキャッシュがちゃんとヒットしているように思われます。
ccache -s |
1回目(Clean → Build) | 2回目(Clean → Archive) |
---|---|---|
ビルド時間(秒) | 25.0sec | 142.1sec |
cache hit (direct) | 2029 (prev:1620) | 2438 |
cache hit (preprocessed) | 5 | 5 |
cache miss | 829 (prev:829) | 829 |
cache hit rate | 71.04 % | 74.66 % |
cleanups performed | 0 | 0 |
files in cache | 3877 | 3877 |
cache size | 1.8 GB | 1.8 GB |
max cache size | 5.0 GB | 5.0 GB |
④の結果
最後にUnity上からReplaceしてxcodeproj
を置き換えた際の結果です。
(ちなみに「既存のxcodeproj
を手動で削除 → ビルド」の手動Replaceも同様の結果です)
こちらはBuild/Archive共に殆どキャッシュヒットしていない様に見受けられました。
ccache
の設定は以下の様になっており、更新チェックに関する条件自体は問題無さそうに思われますが...なぜ適用されていないのかまでは追いきれておらず...。
-
base_dir
は設定していないので、対象は絶対パスで制御される- → Unityから出力される
xcodeproj
は常に同名で出力しており、パス自体は変わっていないので適用されるはず...
- → Unityから出力される
- 更新ファイルのチェック方法は
content(ファイル内容を基にチェック)
で設定- → チェック方法をデフォルトの
mtime(mtimeとsizeのhash)
に戻しても変わらず... - 出力されたIL2CPPのコードにも目立った差分は無い
- → チェック方法をデフォルトの
ccache -s |
1回目(Clean → Build) | 2回目(Clean → Archive) |
---|---|---|
ビルド時間(秒) | 195.6sec | 301.8sec |
cache hit (direct) | 2439 (prev:2438) | 2440 |
cache hit (preprocessed) | 10 | 10 |
cache miss | 1232 (prev:829) | 1640 |
cache hit rate | 66.53 % | 59.90 % |
cleanups performed | 0 | 2 |
files in cache | 5350 | 6576 |
cache size | 2.4 GB | 3.4 GB |
max cache size | 5.0 GB | 5.0 GB |
結果を踏まえて
個人的には「Replace相当のビルドを行った際にもキャッシュヒットしてくれると嬉しい3」と言うモチベーションから調査を開始したこともあったので、今の時点での結果としては少し微妙だった...と言ったところです。
原因については特定しきれていないので、気が向いたら調査を再開していきたい...。
未調査項目
幾つか挙げます。
- Xcode側のビルド設定の詳細把握
- Xcode/ccache共に設定に抜けが無いか確認
- ドキュメントにある「Troubleshooting -> Performance」を読む分にはマクロ周りが影響を及ぼしている可能性も考えられる...?
- 後は「揮発性の情報を含む自動生成コード」と言うのも引っ掛かってそうな感じもあったり...
- 「Cache debugging」にあるデバッグモードを有効にすることで原因調査を進められそう?
- まだ未検証...
補足: Androidは?
「内部的にNDK使ってそうだし、一部のビルドプロセスは高速化出来るかな?」と仮説立てて調べてみましたが...結論から言えばダメそう...?(調査不足の可能性あり)
環境変数のNDK_CCACHE
などを設定した状態でAndroidビルドを回してみましたが、ccache -s
で統計情報を見る分には何も動いていないように思われました。
後はこの状態で[Export Project]
をON/OFFにしてビルドを行い、双方のビルドレポートを確認した感じだと、どちらも支配的なのはIL2CPP
と言うプロセスでした。
こちらが文字通り「ILからCPPに変換するツール側のプロセス」でコンパイラが一体関与しない類のものであれば、そもそもとして適用自体でき無さそうとも考えられそうです。(IL2CPPをコードレベルで追ったわけではないので予想にはなりますが...)
参考/関連サイト
- ccache.dev(公式サイト)
- Ccache - ArchWiki
- ccache でビルド高速化。と設定のポイント
- ccacheを導入してC++のコンパイルを最適化
- ccacheを使って共同作業のビルド時間を改善する
- cocos2d-x(Android NDK)ビルドを高速化する方法とオススメ設定
- C/C++のコンパイルを高速化する|ccache
-
簡単に言えばC/C++に於けるビルド高速化ツール。ざっくりとした概要はWikipediaの記事参照 ↩
-
正確に言うとUnityが出力した
xcodeproj
に対するXcodeビルド ↩ -
例えばビルドプロセスを自動化する際に、ビルド毎に影響が残らないように敢えてReplace相当でビルドすることがあるので、ここでキャッシュヒットして高速化されると嬉しかったりする。 ↩