Edited at

Unity/C#における繰り返し文の速度比較

More than 3 years have passed since last update.

for,while,foreachの速度差を検証してみる

[2016/02/20 追記]

全部まとめて書いてしまったので単品で書いたものとILが異なっている可能性有り

個々で検証を行うとまた結果が違うかも・・・


【検証環境】

・Unity5.2.4f1(Personal Edition)

・Nexus7(Android 5.0.2)

・100万回ループした際にかかった時間を比較


【比較項目】

①forの前置インクリメント

②for

③while

④do-while

⑤foreach

⑥forのLengthチェック

⑦forのList<T>参照

⑧forのList<T>参照のCountチェック

⑨foreachのList<T>参照

⑩whileのLinkedList<T>参照

⑪foreachのLinkedList<T>参照


【テストコード】

using UnityEngine;

using System.Collections.Generic;

public class Test : MonoBehaviour {

void Start() {
int count = 1000000;
int[] array = new int[count];
List<int> list = new List<int>(count);
LinkedList<int> link = new LinkedList<int>();
float prevTime, nowTime;
int hoge;

// 適当なデータを格納
for (int i = 0; i < count; i++) {
hoge = UnityEngine.Random.Range(0, 65535);
array[i] = hoge;
list.Add(hoge);
link.AddLast(hoge);
}

// ① for前置
prevTime = Time.realtimeSinceStartup;
for (int i = 0; i < count; ++i) {
hoge = array[i];
}
nowTime = Time.realtimeSinceStartup;
Debug.LogError("① for前置 : " + (nowTime-prevTime));
// ③ for後置
prevTime = Time.realtimeSinceStartup;
for (int i = 0; i < count; i++) {
hoge = array[i];
}
nowTime = Time.realtimeSinceStartup;
Debug.LogError("② for後置 : " + (nowTime-prevTime));
// ③ while
prevTime = Time.realtimeSinceStartup;
int indexWhile = 0;
while (indexWhile < count) {
hoge = array[indexWhile];
indexWhile++;
}
nowTime = Time.realtimeSinceStartup;
Debug.LogError("③ while : " + (nowTime-prevTime));
// ④ do-while
prevTime = Time.realtimeSinceStartup;
int indexDo = 0;
do {
hoge = array[indexDo];
indexDo++;
} while (indexDo < count);
nowTime = Time.realtimeSinceStartup;
Debug.LogError("④ do-while : " + (nowTime-prevTime));
// ⑤ foreach
prevTime = Time.realtimeSinceStartup;
foreach (int val in array) {
hoge = val;
}
nowTime = Time.realtimeSinceStartup;
Debug.LogError("⑤ foreach : " + (nowTime-prevTime));
// ⑥ for-length
prevTime = Time.realtimeSinceStartup;
for (int i = 0; i < array.Length; i++) {
hoge = array[i];
}
nowTime = Time.realtimeSinceStartup;
Debug.LogError("⑥ for-length : " + (nowTime-prevTime));
// ⑦ for-list
prevTime = Time.realtimeSinceStartup;
for (int i = 0; i < count; i++) {
hoge = list[i];
}
nowTime = Time.realtimeSinceStartup;
Debug.LogError("⑦ for-list : " + (nowTime-prevTime));
// ⑧ for-list-.Count
prevTime = Time.realtimeSinceStartup;
for (int i = 0; i < list.Count; i++) {
hoge = list[i];
}
nowTime = Time.realtimeSinceStartup;
Debug.LogError("⑧ for-list.Count : " + (nowTime-prevTime));
// ⑨ foreach-list
prevTime = Time.realtimeSinceStartup;
foreach (int val in list) {
hoge = val;
}
nowTime = Time.realtimeSinceStartup;
Debug.LogError("⑨ foreach-list : " + (nowTime-prevTime));
// ⑩ while-link
prevTime = Time.realtimeSinceStartup;
LinkedListNode<int> node = link.First;
while (node != null) {
hoge = node.Value;
node = node.Next;
}
nowTime = Time.realtimeSinceStartup;
Debug.LogError("⑩ while-link : " + (nowTime-prevTime));
// ⑪ foreach-link
prevTime = Time.realtimeSinceStartup;
foreach (int val in link) {
hoge = val;
}
nowTime = Time.realtimeSinceStartup;
Debug.LogError("⑪ foreach-link : " + (nowTime-prevTime));
}
}


【結果】

① for前置 : 0.01135254

② for後置 : 0.01126099

③ while : 0.01022339

④ do-while : 0.01327515

⑤ foreach : 0.01751709

⑥ for-length : 0.01556396

⑦ for-list : 0.02728271

⑧ for-list.Count : 0.03369141

⑨ foreach-list : 0.03857422

⑩ while-link : 0.01895142

⑪ foreach-link : 0.03387451


【総評】

①~④については誤差レベル

試行回数を増やして平均値とれば差もなくなりそうな感じ

Unity/C#ではインクリメントも前置が後置より必ず早いということは見受けられなかった

(そもそもILレベルで差分が出てるのだろうか・・・)

また固定配列の.Lengthは思ったよりコストが高くなかった

神経質に配列長をキャッシュする必要は必ずしもないのかもしれない

foreachは速度の問題よりイテレータのインスタンスでヒープを食うのでランタイムでは使うべきでない

Editor拡張スクリプト等で使うべきだろう

List<T>については基本的に固定配列より参照が遅い

CapacityオーバーやInsert等のコストは当然高くメモリも余剰に食うので

特にListである必要がなければ固定配列にしたい

ただSortなど色々容易なのでランタイムでも使いたくなる場面はままある

おまけのLinkedListだがwhileで始端から終端をなめる分には速度は問題ない

しかしAddの際にノードのインスタンスを作るのでヒープを食ってしまう

GCが容認しづらい環境下での重用は難しい