C＃コンパイルの最適化

今回の主な話題：C＃コンパイルの最適化、IPAパッケージサイズの問題、Unityパッケージのスピードアップ、AssetBundleのインクリメンタルパッケージ化。

---
###アセット管理
Q1: 現在、プロジェクトのAPKサイズは約120MBで、パッケージ化の平均時間は20〜25分です。Build中のほとんどの時間は「Building Scene0」と「Building additional assets」二パートにかかります。Unityはこれら2つの段階で一体何をしていますか？この段階で時間を節約する方法はありますか？また、ハードウェアから考える場合、パッケージ速度を効果的に向上させる方法は？ハードディスクのR/W速度とCPUのパフォーマンスを向上させることに、どのようなメリットがもたらされますか？
>私たちはAssetBundleの方式を使っています。現在、600MBくらいのパッケージ、アセットをあまり変更しない場合、Androidパッケージは約10分で出来ますが、前には7〜8分で十分でした。C＃コードを変更しません、ただのPatchなら5〜7分くらいで出来ます。

>私たちが使用しているAndroidパッカーの価格は約150,000です。これには複数のCPUが搭載されており、シングルコアCPUのパフォーマンスが可能な限り高いことが保証されています。SDDが必要であり、その他は不要です。

>使っているCPUは：Intel（R）Xeon（R）CPU E5-2683 v3 @ 2.00GHz 2.00 GHz

>テクスチャ圧縮についても注意すべき点があります。以前はテクスチャ圧縮によってよく苦しさめていました、高品質は遅すぎます。後で、DevとPubのバージョンを区別しました。Devバージョンはローカルテクスチャ圧縮にFastを使用し、公開に使用されるPubバージョンは最高品質を使用します。速度は相当早くなりました〜アーティストに多くのテクスチャ変更がある場合でも、Devバージョンはすぐにパッケージ出来ます。

>私はずっと前にGithubでこれを見つけました。
pvrtextool_wrapper：https://github.com/fxgames/pvrtextool_wrapper

>Unityのパッケージ化はpvrtextoolをコールします。このwrapperで元のプログラムを置き換えますと、開発バージョンの 圧縮品質を強制的に高速モードに設定でき、テクスチャ圧縮品質パラメーターを変えずに大量の時間を節約できます。Mac Proが高すぎると思っている方は、この方法を試すことができます。

>Building SceneはUnityのパッケージシーンアセットであり、Building additional assetsはUnityがResourcesをパッケージする中のアセットであります。私たちのプロジェクトは600 MBに近く、毎回パッケージ化には50分以上かかるため、悲惨です。下記の方法をやってみられます：
1）AssetBundleの形式ですべてのアセットを使用します。初回以外、次回からパッケージされたのは変更と増分のあるアセットのみです。なぜなら、Resourcesの毎回の出版はアセットを再びパッケージしますからです。これで変更されていないアセットのパッケージ化の時間コストを節約出来ます。
2）良質なコンピューターを作り、CPUをより強いCPUに変え、ハードディスクをSSDに交換します。

>上記の2つのポイントにより、パッケージを出す時間を節約できます。これも、弊社現在使用している方法です。

---
###アセット管理
Q2：パッケージ化を二回しました、どちらのファイルも変更されていませんが、AssetBundleの内容は変更されました。その原因は、そこに含まれるスクリプトの内容に違いがあるように感じます。Manifestファイルには、CRCとAssetFileHashに変更がありますと表示しています。ファイルはもうUWA Q＆A公式Webサイトにアップロードされています。最初のパッケージは完全パッケージで、二番目のパッケージは増分パッケージです。

完全パッケージの流れは下記のように：
1、Revertアセット
2、 すべての BundleNameのアセットをClearします
3、規則によって全体のBundleNameを設定します
4、 BuildPipeline.BuildAssetBundles(outputPath, abBuilds, BuildAssetBundleOptions.ChunkBasedCompression, EditorUserBuildSettings.activeBuildTarget);を使用してパッケージします
5、ファイルに対してBundleNameのマッピングテーブルを生成します

増分パッケージの流れは下記のように：
1、Revertアセット
2、 すべての BundleNameのアセットをClearします
3、マッピングテーブルによってアセットBundleNameを設定します
4、規則によって他のアセットのBundleNameを設定します
5、BuildPipeline.BuildAssetBundles(outputPath, abBuilds, BuildAssetBundleOptions.ChunkBasedCompression, EditorUserBuildSettings.activeBuildTarget);を使用してパッケージします
6、ファイルに対してBundleNameのマッピングテーブルを生成します
>主な問題は、パッケージする前にUIのPrefabを事前処理すると、一部のコントロールにスクリプトコンポーネントを追加したことが分かりました。この変更されたPrefabはアップロードされないため、毎回にこの操作を行います。毎回追加されるスクリプトコンポーネントに割り当てられるFileIdは異なりますから、毎回異なるファイルになります。チェックしたときにこの部分が無視されました。

>また、Unity 5.x以降のBuildAssetBundleOptions.DeterministicAssetBundleがデフォルトのオプションであり、特に追加する必要がないことがUnity公式に確認されました。

---
###その他
Q3：出力されるAppStore.ipaがDevelopment.ipaより相当大きいです。なぜこれが発生するのか理解したいと思います。

出力環境：
xcode：9.2
unity:5.6.3p2

データのスクリーンショット：
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/bf690ba2-6aa2-c1cc-dce3-ae48c6f636bb.png)
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/b406c072-9ea7-c3af-781d-959ce5ec33b7.png)
まず、コードはswift + obj-cメソッドを使用し、エクスポート時にALWAYS_EMBED_SWIFT_STANDARD_LIBRARIESがYesに設定されます。スクリーンショットからわかるのは、主にはAppStoreがDevelopmentよりSwiftSupportの内容を増加します。私がパッケージしたAppStore.ipaはDevelopment.ipaより約50MB大きくなっています。 この問題をどのように回避できますか？
>AppStoreパッケージにはデフォルトでsymbolsを添付しており、クラッシュログをシンボル化するために使用され、アップロード後に削除されます。最終、ユーザーがダウンロードしますのはこのIPAのサイズではありません。

---
###その他
Q4：C＃に一つの関数内容がマクロ定義で囲まれ、コンパイル時に空き関数になりますが、外部からのコールがあり、コンパイル時にこのコールを最適化しませんか？たとえば、入力パラメーターは、いくつかの関数コールを行ってから戻り値を渡すことです。この操作のコストはそのままありますが、定数を入れば、コンパイルされて最適化されますか？
①
>コンパイラーは、マクロでラップしたコードのみを最適化し、対応するマクロは定義されません。

>例えば：

>if UNITY_EDITOR
Debug.Log(“hello uwa”);
else if UNITY_iOS
Debug.Log("good uwa");
endif
if UNITY_EDITORにのはエディターでのみ有効になり、パッケージ化後に最適化されます。

②
>[Conditional]

上記の回答によると、IL2CPPでマクロ定義と条件付きコンパイルの違いを測定しました。コードは下記のように。
コンパイル後に生成されるC ++コードは図のように
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/89d0ba6f-c335-0e57-46cf-1e9ba2f1c7af.png)
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/4c0494de-e3ce-c993-9cac-78b094d8b919.png)
>どちらも関数の定義を生成することがわかりますが、違いのは：
（1）マクロ定義は関数定義のコードを削除しますが、コールサイトでのパラメーター転送は最適化されないため、関するコールコストを完全に排除することはできません。
（2）条件付きコンパイルは、関数のコールステートメントを直接削除し、関するコールオーバーヘッドが生成されなくなりますが、関数定義のコードはそのまま残ります。

---
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、**パフォーマンス分析**と**最適化ソリューション**及び**コンサルティングサービス**を提供している会社でございます。

UWA公式サイト：https://jp.uwa4d.com
UWA公式ブログ：https://blog.jp.uwa4d.com