配列とIEnumerable, IReadOnlyList での処理速度差

C# で配列を確保した後に、それを IEnumerable や IReadOnlyList で受けて処理をした場合にどの程度の速度差があるのかを計ってみた。.NET 4.6, x64環境での調査。

100000000要素での結果は、

Array
87 ms.
IEnumerable
624 ms.
IReadOnlyList
568 ms.

ということで、やっぱり生配列だとぶっちぎりで早い。速度が重要なら、 IEnumerableなどで受けても内部で配列にキャストしてから処理する特別ルーチンを入れたりすることは考えても良いのかも知れない。

以下、利用したコード:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace ColTest
{
  class CollectionSpeedTest
  {
    public static void Main(string[] args)
    {
      var count = int.Parse(args[0]);
      var arr = random().Take(count).ToArray();
      using (new StopwatchHelper("Array"))
        TestArray(arr);
      using (new StopwatchHelper("IEnumerable"))
        TestIEnumerable(arr);
      using (new StopwatchHelper("IReadOnlyList"))
        TestIReadOnlyList(arr);
    }

    static double TestArray(double[] arr)
    {
      double sum = 0;
      for (int i = 0; i < arr.Length; i++)
        sum += arr[i];
      return sum;
    }

    static double TestIEnumerable(IEnumerable<double> en)
    {
      double sum = 0;
      foreach (var v in en)
        sum += v;
      return sum;
    }

    static double TestIReadOnlyList(IReadOnlyList<double> col)
    {
      double sum = 0;
      for (int i = 0; i < col.Count; i++)
        sum += col[i];
      return sum;
    }

    static IEnumerable<double> random()
    {
      var random = new Random();
      for (;;)
      {
        yield return random.NextDouble();
      }
    }

    class StopwatchHelper : IDisposable
    {
      public StopwatchHelper(string format, params string[] args)
      {
        Console.WriteLine(format, args);
        sw.Start();
      }

      public void Dispose()
      {
        sw.Stop();
        Console.WriteLine("{0} ms.", sw.ElapsedMilliseconds);
      }

      Stopwatch sw = new Stopwatch();
    }
  }
}