【Unity】Unite 2015「Unity パフォーマンス・チューニング」レポート


# レポート一覧

- [Unity パフォーマンス・チューニング](http://qiita.com/baba_s/items/c536b923b6cdd919e73e)
- [Unityプロジェクトをひも解き把握するには](http://qiita.com/baba_s/items/446fa7a040f09bcd0ac6)
- [エディター拡張マニアクス 2015](http://qiita.com/baba_s/items/8abfebb18f43fcc5d319)
- [MOBIUS FINAL FANTASYにおけるUnity開発事例](http://qiita.com/baba_s/items/ce6b11a5a8952ecf2485)
- [白猫プロジェクトの裏側！～パフォーマンスチューニングとリアルタイム通信の全て～](http://qiita.com/baba_s/items/7c5680b4b808defddcb7)
- [Unity × Sprite Animation](http://qiita.com/baba_s/items/a410ca070a2d3d037ff8)
- [Unityを利用したスマートフォン向けゲームアプリ開発へのアプローチ方法](http://qiita.com/baba_s/items/34d97d970b7f8b17a568)

# 講演ファイル

http://japan.unity3d.com/unite/unite2015/files/DAY2_1030_room1_Kim.pdf

# LOADING - ASYNCHRONOUSLY

- 非同期読み込みのAPI
	- Applicalion.LoadLevelAsync
	- AssetBundle.LoadAsync
	- Resources.LoadAsync(5.0)

# LOADING SCENES - MEMORY

- シーン遷移時は古いシーンと新しいシーンが同時にメモリに載る
	- 新しいシーンを読み込む前に空のシーンを読み込むことで回避できる
	- ただし、デメリットもある
	- 古いシーンで使っていて新しいシーンでも使う予定のアセットをアンロードしてしまう

# LOADING - SERIALIZATION

- Unityは独自のシリアライズの機能を持っている
- Unityのシリアライズは高速
	- 下位互換性があるがそちらのスピードは遅い
- もともとはかなり遅かった
	- リフレクションをその場でやっていたから
	- 4.5以降で改善

# LOADING - ASSETBUNDLE


- 自分の好きな形でパッキングできる
- サイズの最適化ができる
- プレイヤー側で制約がある場合に有用
- ビルド時間の最適化になる
- ローカル、Webサーバから読み込める
- `WWW.LoadFromCacheOrDownload`  
↑これを使ってくださいとのこと

# UNLOADING

- 5.0においてオブジェクトの削除が改善された
	- オーバーヘッドが小さくなりました
	- クリーンナップがバックグラウンドで行われる
	- アセットはGCが走るまで残っている
	- ガタツキに繋がらないようにいいタイミングで使うこと
- 主導でGCを起動してもいい
	- `GC.Collect`
	- `Resources.UnloadUnusedAssets`
- 安全な仕組みがない
	- アセットが使われているかどうかを判断する機能がない
	- エディタなら依存関係がわかるが・・・

# GC PERFORMANCE

- `GC.Collect`によってスパイクが起きる
- 仕組みを理解しよう
	- Mark and sweep型
	- staticオブジェクトからスタック、ヒープをスキャンしていく
	- 分散しているとランダムアクセスで何度もアクセスしてしまう

# GC OPTINMATIONS

- GCがあまり動かないようにするためには
	- データのレイアウトを考えること
	- リファレンスが密に行われること
	- classではなくstructを使用すること
- GCが起動する理由
	- ヒープのメモリが不足している場合
	- ヒープは残っているけど割り当てがたくさんある場合

# REDUCE GC WORK

- リファレンスするようなデータをパックする
- classの代わりにstructにするとすべてインラインになる
- classはフラグメンテーションが起きてしまう
- structのほうがキャッシュパフォーマンス的にいい
- データを順番に並べる

```cs
class Character
{
	public string Name;
	public Vector3 Pos;
}
Character[] characters;
```

```cs
struct Character
{
	public string Name;
	public Vector3 Pos;
}
Character[] characters;
```

# RESULTS

- GC for array length 1,000,000 characters on MBP
	- Class with randomized memory locations: 35ms
	- Class with linear memory: 20ms
	- Structs: 10ms
	- Struct and no String: 0.18ms
- stringを完全に無くすとかかった時間が短縮された

# MINIMIZING MANAGED ALLOCATIONS

- OnGUIは使わないこと

# AVOIDING ALLOCATIOS

- 関数の中で配列の割り当てをせずプールすること

# POOLING

- 事前にプールの割り当ては行っていくように

# POOLING - HOW?

- SetActiveでオブジェクトを使い回す
- Dictionaryは使わず配列を使うように
- 5.0においてインスタンス化の時間が改善、破棄も改善
- SetActiveとかも50%くらい速度が改善されている

# GENERAL PERFORMANCE

- Findは使わないように
	- Inspectorで設定することでスピードが上がる、  
安定する、検索漏れがなくなる
- 過剰な遷移を避ける
- MonoBehaviourのUpdateの数を減らす
	- 1,000個のMonoBehaviourのUpdateを呼び出すのではなく  
1つのMonoBehaviourのUpdateから1,000個のUpdateを呼び出す
	- 1つのゲームオブジェクトで複数のコンポーネントを管理するイメージ
- Componentとかゲームオブジェクトはキャッシュする
	- すべてMonoBehaviourを使う必要はない
	- MonoBehaviourはかなり負荷が高い
	- 通常のC#を使うこと
	- ゲームオブジェクトも同じことがいえる
	- positionとかtransformが必要だったとしても  
C#でクラスを用意することも必要
	- Unityの機能をすべて使わなくてもいい
- たくさんの子オブジェクトの更新は負荷が高い
	- エディタ上でちゃんとHierarchyを整理しよう
	- transformのオーバーヘッドに注意

# まとめ

- 非同期でロードしよう
- オブジェクトを再利用すること
- キャッシュして繰り返し作業しないように
- 自分が何を実装しているのかによってボトルネックが何かは変わってくるので  
ちゃんとプロファイラーをみて改善策を選択すること
	- 他の人のアドバイスを聞くよりも自分のゲームのプロファイラを信じること