遅いよコレクション

Genericsがサポートされる前は、動的配列を使おうとするとArrayListあたりを使うことになりました。しかし、ArrayListはObjectのコレクションのため、プリミティブ型を入れるとboxing/unboxingが発生し、パフォーマンスが良くありませんでした。.NET2.0ではGenericsに対応したコレクションとしてListが用意され、大幅なパフォーマンスアップが期待できそうです。で、早速、テスト。

#region Using directives

using System;
using System.Collections;
using System.Collections.Generic;

#endregion

delegate void M();

class MyArray<T>
{
    public T data;
    public int count = 0;

    public MyArray() : this(16) { }
    public MyArray(int capacity)
    {
        data = new T[capacity];
    }

    public int Count
    {
        get { return count; }
    }

    public T this[int index] {
        get { return data[index]; }
        set { data[index] = value; }
    }

    public void Add(T t)
    {
        if (data.Length <= count)
        {
            T tmp = new T[data.Length * 2 + 1];
            Array.Copy(data, tmp, data.Length);
            data = tmp;
        }
        data[count++] = t;
    }

}

class Program
{
    const int loop = 1024 * 64;
    static void Array()
    {
        byte a = new byte[loop];
        for (int i = 0; i < a.Length; ++i)
        {
            for (int j = a.Length - 1; j >= 0; --j)
            {
                a[i] = a[j];
            }
        }
    }

    static void ArrayList()
    {
        ArrayList a = new ArrayList(loop);
        for (int i = 0; i < loop; ++i)
            a.Add(0);
        for (int i = 0; i < a.Count; ++i)
        {
            for (int j = a.Count - 1; j >= 0; --j)
            {
                a[i] = a[j];
            }
        }
    }

    static void List()
    {
        List<int> a = new List<int>(loop);
        for (int i = 0; i < loop; ++i)
            a.Add(0);
        for (int i = 0; i < a.Count; ++i)
        {
            for (int j = a.Count - 1; j >= 0; --j)
            {
                a[i] = a[j];
            }
        }
    }

    static void MyArray()
    {
        MyArray<int> a = new MyArray<int>(loop);
        for (int i = 0; i < loop; ++i)
            a.Add(0);
        for (int i = 0; i < a.Count; ++i)
        {
            for (int j = a.Count - 1; j >= 0; --j)
            {
                a[i] = a[j];
            }
        }
    }

    static void MyArray2()
    {
        MyArray<int> a = new MyArray<int>(loop);
        for (int i = 0; i < loop; ++i)
            a.Add(0);
        for (int i = 0; i < a.count; ++i)
        {
            for (int j = a.count - 1; j >= 0; --j)
            {
                a.data[i] = a.data[j];
            }
        }
    }

    static void Time(M m)
    {
        DateTime t = DateTime.Now;
        m();
        Console.WriteLine(m.Method.Name.PadRight(10) + ":" + DateTime.Now.Subtract(t));
    }

    static void Main(string args)
    {
        Time(Array);
        Time(ArrayList);
        Time(List);
        Time(MyArray);
        Time(MyArray2);
        Console.ReadLine();
    }
}

/* 結果
Array     :00:00:07.5312500
ArrayList :00:02:37.9687500
List      :00:01:00.7812500
MyArray   :00:00:10.2968750
MyArray2  :00:00:13.9375000
 */

やはり、ArrayListに比べるとListは倍以上速いですね!
・・・あ、いや・・・Arrayに比べると、もー、どっちでも良いよって気になりそ。(^^; まぁ、ArrayListもListもインタフェースベースのコレクションなので、メソッド呼び出しがインライン化されないのが原因なんだろうなぁ。固定長配列だと使いにくいのでMyArrayなんかこさえてみたら、若干パフォーマンスが悪くなりました。プロパティ経由でアクセスしているからなのかと、メンバーをpublicにして直接アクセスさせたら、かえって遅くなる結果に。ということは、クラスのメンバーにしているのが原因かな。(プロパティ経由の方が速いというのもちょっと予想外ですが)