LoginSignup
7
5

More than 1 year has passed since last update.

ccacheを導入してUnityのiOSビルドを高速化出来ないか検証してみた

Last updated at Posted at 2020-05-24

ccache1がUnityのiOSビルド2でも利用できるのか?」「また、出来るとした場合にはどれくらい効果があるのか?」について調べてみました。

私自身、ccacheの導入そのものが初だったこともあるので、前半は主にccacheのインストール方法や設定項目」などについて記載していき、後半から「Unity上での導入手順及び効果」について記載していければと思います。
(予めccacheを知っている方は前半部分読み飛ばしても良いかも)

もし間違いや不足などあればコメントにて教えて頂けると幸いです! :bow:

※まだ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から変更できます。
単位はGMなど付ければ指定可能です。

# -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

こちらを直接編集することでも設定変更が可能です。

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の書き換え

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ビルドにて導入する際の手順や結果などについて纏めていきます。

導入手順

導入手順については以下の記事を参考にさせていただきました。
順に解説していきます。

ccacheを導入してC++のコンパイルを最適化

ちなみに、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

ビルド時間の計測

ビルドに用いる検証用プロジェクトには、以前書いたこちらの記事にあるゲームプロジェクトを利用します。
(実案件レベルとは行かずとも、コンパイル時間を検証するにあたって多少のコード量は欲しかったので)

mao-test-h/DOTS-Jungle

環境/スペック

  • macOS Catalina 10.15.3
    • MacBook Pro (15-inch, 2018)
      • CPU: 2.2 GHz 6コア Intel Core i7
      • メモリ 16GB 2400 MHz DDR4
  • 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は常に同名で出力しており、パス自体は変わっていないので適用されるはず...
  • 更新ファイルのチェック方法は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」を読む分にはマクロ周りが影響を及ぼしている可能性も考えられる...?
    • 後は「揮発性の情報を含む自動生成コード」と言うのも引っ掛かってそうな感じもあったり... :thinking:
  • 「Cache debugging」にあるデバッグモードを有効にすることで原因調査を進められそう?
    • まだ未検証...

補足: Androidは?

「内部的にNDK使ってそうだし、一部のビルドプロセスは高速化出来るかな?」と仮説立てて調べてみましたが...結論から言えばダメそう...?(調査不足の可能性あり)

環境変数のNDK_CCACHEなどを設定した状態でAndroidビルドを回してみましたが、ccache -sで統計情報を見る分には何も動いていないように思われました。

後はこの状態で[Export Project]をON/OFFにしてビルドを行い、双方のビルドレポートを確認した感じだと、どちらも支配的なのはIL2CPPと言うプロセスでした。
こちらが文字通り「ILからCPPに変換するツール側のプロセス」でコンパイラが一体関与しない類のものであれば、そもそもとして適用自体でき無さそうとも考えられそうです。(IL2CPPをコードレベルで追ったわけではないので予想にはなりますが...)

EXTuVImUYAA6TjB.png

参考/関連サイト


  1. 簡単に言えばC/C++に於けるビルド高速化ツール。ざっくりとした概要はWikipediaの記事参照 

  2. 正確に言うとUnityが出力したxcodeprojに対するXcodeビルド 

  3. 例えばビルドプロセスを自動化する際に、ビルド毎に影響が残らないように敢えてReplace相当でビルドすることがあるので、ここでキャッシュヒットして高速化されると嬉しかったりする。 

7
5
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5