前振り
iPhone5SやAndroid4.4, 5などで発売された端末でやけにもっさりして、
パフォーマンスの見直しが必要だと思った方に見てもらいたい内容です。
そして、今回のテーマは「的確なLoop処理の対応について」です。
環境
Unity 5.5以降 5.6未満
loop処理の改善
【それぞれのloop処理について】
- 型[] array = new 型();
- List<型> list = new List<型>();
- Dictionary<型, 型> dic = new Dictionary<型, 型>();
- IEnumerator enu;
【対応方法】
上記の4つのloop処理は、以下のような対応が必要となります。
1.arrayの対応
for(int i = 0; i < array.Length; i++) {
var data = array[i];
}
2.listの対応
for(int i = 0; i < list.Count(); i++) {
var data = list[i];
}
3.dicの対応
foreach(var value in dic.Keys) {
// keyが欲しいとき
}
foreach(var value in dic.Values) {
// valueが欲しいとき
}
4.enuの対応
var list = enu.GetEnumerator();
try {
while(list.MoveNext()) {
var data = list.Current;
}
}
finally {
list.Dispose();
}
【解説】
4の対応についてですが、一般的には、foreachでloop処理して中身を取り出すことが多いです。
C#の解説でもよく見かけますし、特に変わりはないとされていましたが、Unity C#となると話は別です。
using展開のコンパイラバグ
「Listをforeachで回すとGCゴミが出るのはUnityのコンパイラが古いせいでバグッてるから」というのが良く知られている話ですが、
より正しい理解に変えると、「構造体のIDisposableに対するusingの展開結果が最適化されていない(仕様に基づいていない)」ということになります。
この辺の話はECMA-334 C# Language Specificationにも乗っているので、C#コンパイラの仕様に対するバグと言えるのではないか。
※参考記事に書かれている内容です。
【参考記事】
C#のGCゴミとUnity(5.5)のコンパイラアップデートによるListのforeach問題解決について
http://neue.cc/2016/08/05_537.html
C# Dictionary のループのパフォーマンス
https://www.dcom-web.co.jp/lab/cs/dictionary-loop
おまけ
ちなみにforeachの対応前と対応後になります。
そんなに変わりませんが、対応前よりもCPU Usageで60FPSをギリギリ超えなくなっていますね。
↓foreach対応前
おまけ2
テラシュールブログの山村さんの記事で、以下の対応策が挙げられていました。
・localPositionを使う
・Vectorのoperationを減らす
・VectorのMathfを使用しない
・transformのキャッシュ
・エンジンコードの呼び出し削減
・time.deletaTimeの削減
・foreachを使用しない ← 今回のお話し
・一覧でアクセスするなら、Listではなく、Arrayを使用する
・NonAllocと名のつくAPIを使う(RaycastNonAlloc等)
・stringの結合は避ける(GCの発生を避ける)
・UpdateやLateUpdate等のコールバックを減らす
参考記事: 【Unity道場】パフォーマンス最適化の
ポイント
https://speakerdeck.com/unitydojo/unitydao-chang-pahuomansuzui-shi-hua-false-pointo