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が容認しづらい環境下での重用は難しい