読者です 読者をやめる 読者になる 読者になる

WonderPlanet DEVELOPER BLOG

ワンダープラネットの開発者ブログです。モバイルゲーム開発情報を発信。

Unity C# メソッド呼び出し方法による速度の違い

C-Sharp Unity

今回のエンジニアブログを担当する加賀です。

UnityでC#のLINQを使っているとき、
何度もメソッド呼び出しがされるので速度が遅くなるのではないか?
という疑問が出来たので、計測してみました。

計測環境

  • Mac OS X 10.9.5
  • Intel Core i5 1.3GHz
  • Unity 5.1.2

基本コード

ランダムな0~999までの10,000,000個の整数の中から、ランダムな値が何個あるかをカウントする処理です。
なお、生成される乱数の順序を同じにするために、seed値を指定しています。
100回計算して、最小と最大の値を除いた98回の平均値を時間とします。
以下のCalcTime関数の中身を変えて計測してみます。

public class Test : MonoBehaviour {  
  List<int> list;  
  bool isFinish;  
  
  void Start() {  
    isFinish = false;  
    list = new List<int> ();  
    UnityEngine.Random.seed = 12345;  
    for(int i = 0; i < 10000000; i++) {  
      list.Add (UnityEngine.Random.Range (0, 1000));  
    }  
  }  
  
  void Update() {  
    if (!isFinish) {  
      List<int> time = new List<int> ();  
      for (int i = 0; i < 100; i++) {  
        DateTime start = DateTime.Now;  
        CalcTime ();  
        DateTime end = DateTime.Now;  
        TimeSpan span = end - start;  
        time.Add (span.Milliseconds);  
      }  
      int min = time.Min ();  
      time.Remove (min);  
      int max = time.Max ();  
      time.Remove (max);  
      double avg = time.Average ();  
      Debug.Log ("min:" + min + " max:" + max + " avg:" + avg);  
      isFinish = true;  
    }  
  }  
  
  void CalcTime() {  
    int count = 0;  
    int search = UnityEngine.Random.Range (0, 1000);  
    // ここ以下を変えます  
  }  
}  

for文(参考)

int listSize = list.Count;  
for (int i = 0; i < listSize; i++) {  
  if (list[i] == search) {  
    count++;  
  }  
}  

foreach文

foreach (int val in list) {  
  if (val == search) {  
    count++;  
  }  
}  

foreach文と同等なコード

var e = list.GetEnumerator ();  
while (e.MoveNext ()) {  
  if (e.Current == search) {  
    count++;  
  }  
}  

ラムダ式

count = list.Count (val => val == search);  

一旦delegate変数に入れたラムダ式

Func<int, bool> func = val => val == search;  
count = list.Count (func);  

測定結果

処理方法 結果(ミリ秒) 5回平均(ミリ秒)
for文(参考) for文の結果 172.1122448979592
foreach文 foreach文の結果 457.5408163265306
foreach文と
同等なコード
foreach文と同等なコードの結果 441.4469387755102
ラムダ式 ラムダ式の結果 615.369387755102
一旦delegate変数に
入れたラムダ式
一旦delegate変数に入れたラムダ式の結果 607.304081632653

結果から

for文は安全性を犠牲に速度を優先しているのであまり比較対象には出来ませんが、
foreach文で自力で実装するのと、LINQを使用するのとで結構な差が出ることがわかりました。
UnityのLINQ実装は公開されているので、内部コードと自力実装のforeach文とを見比べてみると、
引数のnullチェック関数やセレクター(今回はカウント対象かどうかの判定関数)の呼び出しで時間がかかっていると思います。
ラムダ式をデリゲート変数に入れても、無視できる差しか縮まらないのを見ても、関数呼び出しが重い処理というのがわかります。

iOSでの制約で出来ない場合もありますが、同じことを複数のLINQメソッドで記述するよりも、
出来る限りLINQメソッドをまとめた方が早くなることがわかりました。
LINQはやりたい事が簡潔に記述できて便利ですが、よく気をつけて使用していきたいと思います。