それで処理速度を計測するコードを書いてみたらよく分からない結果になった。
using System;ksksts / junk / source — bitbucket.org
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace IListEnumerationBenchmark
{
class Program
{
static void Main(string[] args)
{
var size = 1000000;
var count = 10;
var sw = new Stopwatch();
Action<IList<int>> run = list =>
{
var useIndexerTicks = 0L;
var useEnumeratorTicks = 0L;
var useForeachTicks = 0L;
for (var c = 0; c < count; c++)
{
var random = new Random(c);
for (var n = 0; n < list.Count; n++)
list[n] = random.Next(int.MinValue, int.MaxValue);
// use indexer
sw.Reset();
sw.Start();
var useIndexerResult = UseIndexer(list);
sw.Stop();
useIndexerTicks += sw.ElapsedTicks;
// use enumerator
sw.Reset();
sw.Start();
var useEnumeratorResult = UseEnumerator(list);
sw.Stop();
useEnumeratorTicks += sw.ElapsedTicks;
// use foreach
sw.Reset();
sw.Start();
var useForeachResult = UseForeach(list);
sw.Stop();
useForeachTicks += sw.ElapsedTicks;
// correctness
if (useIndexerResult != useForeachResult)
Console.WriteLine("useIndexerResult: {0}, useForeachResult: {1}", useIndexerResult, useForeachResult);
if (useEnumeratorResult != useForeachResult)
Console.WriteLine("useEnumeratorResult: {0}, useForeachResult: {1}", useEnumeratorResult, useForeachResult);
}
Console.WriteLine("use indexer: {0} ({1:F})", useIndexerTicks / count, useIndexerTicks / (double)useForeachTicks);
Console.WriteLine("use enumerator: {0} ({1:F})", useEnumeratorTicks / count, useEnumeratorTicks / (double)useForeachTicks);
Console.WriteLine("use foreach: {0} ({1:F})", useForeachTicks / count, useForeachTicks / (double)useForeachTicks);
Console.WriteLine();
};
// int[]
{
Console.WriteLine("int[]: ");
var array = new int[size];
run(array);
}
// List<int>
{
Console.WriteLine("List<int>: ");
var list = new List<int>();
for (var n = 0; n < size; n++)
list.Add(0);
run(list);
}
}
static int UseIndexer(IList<int> list)
{
var result = 0;
for (var n = 0; n < list.Count; n++)
result = unchecked(result + list[n]);
return result;
}
static int UseEnumerator(IList<int> list)
{
var result = 0;
var e = list.GetEnumerator();
while (e.MoveNext())
result = unchecked(result + e.Current);
return result;
}
static int UseForeach(IList<int> list)
{
var result = 0;
foreach (var x in list)
result = unchecked(result + x);
return result;
}
}
}
結果は次の通り。
配列ではインデクサでのアクセスがforeachより遅いのにList<t>では逆になるのが意味が分からない。あと両方でGetEnumeratorでのアクセスがforeachよりも速いのも意味が分からない(foreachの方が最適化する余地があるんじゃないかと思う)。
こんな処理にフォーカスした速度なんかほとんどのケースで無視していいだろうから、インデクサでのアクセスが必要じゃない処理では常にforeachを使うけど。
書きながらList<t>.ForEachを思い出した(IList<t>には無い)。使える場合ならforeachよりもそれを使うと思う。ILとかコンパイル後のコードはたぶん見ない。
int[]:追記: 上の結果はIList<T>を通した結果だから不正確。ksksts's blog: 配列やList<T>をIList<T>型のインデクサでアクセスするとパフォーマンスが悪い(don't use IList<T>'s indexer)を参照。
use indexer: 578500 (5.57)
use enumerator: 87133 (0.84)
use foreach: 103895 (1.00)
List<int>:
use indexer: 132922 (0.76)
use enumerator: 154705 (0.89)
use foreach: 174277 (1.00)
0 件のコメント:
コメントを投稿