Struct vs Class

C#Javaと違うところの1つにstruct(値型)をサポートが上げられます。勿論、Javaでもbyteやintなどのプリミティブなものは値型ですが、ユーザ定義の値型はありません。さて、structを使うとパフォーマンスが良いとか、サイズが大きくなったら遅くなるとか言われていますが、実際、どんな感じなんでしょうか? と、いうことで実験してみました。

#region Using directives

using System;
using System.Collections.Generic;

#endregion

namespace ConsoleApplication1
{
    struct StructX
    {
        public int x;
    }

    class ClassX
    {
        public int x;
    }

    struct StructXY
    {
        public int x;
        public int y;
    }

    class ClassXY
    {
        public int x;
        public int y;
    }

    struct StructXYZ {
        public int x;
        public int y;
        public int z;
    }

    class ClassXYZ
    {
        public int x;
        public int y;
        public int z;
    }

    class Program
    {
        const int SIZE = 1000000;
        static void Add<T>(int n, ref List<T> list) where T : new()
        {
            DateTime t = DateTime.Now;
            for (int i = 0; i < n; ++i)
                list.Add(new T() );
            string s = String.Format("Add<{0}>", typeof(T).Name);
            Console.WriteLine(s.PadRight(18) + DateTime.Now.Subtract(t) );
        }

        static void Call<T>(T t)
        {
        }

        static void Call<T>(List<T> list)
        {
            DateTime t = DateTime.Now;
            for (int j = 0; j < 100; ++j)
            {
                for (int i = 0; i < list.Count; ++i)
                {
                    Call<T>(list[i]);
                }
            }
            string s = String.Format("Call<{0}>", typeof(T).Name);
            Console.WriteLine(s.PadRight(18) + DateTime.Now.Subtract(t) );
        }

        static void CallRef<T>(ref T t)
        {
        }

        static void CallRef<T>(ref T list)
        {
            DateTime t = DateTime.Now;
            for (int j = 0; j < 100; ++j)
            {
                for (int i = 0; i < list.Length; ++i)
                {
                    CallRef <T>(ref list[i]);
                }
            }
            string s = String.Format("CallRef<{0}>", typeof(T).Name);
            Console.WriteLine(s.PadRight(18) + DateTime.Now.Subtract(t) );
        }


        static void Main(string args)
        {
            List<StructX> sx = new List<StructX>();
            List<ClassX> cx = new List<ClassX>();
            List<StructXY> sxy = new List<StructXY>();
            List<ClassXY> cxy = new List<ClassXY>();
            List<StructXYZ> sxyz = new List<StructXYZ>();
            List<ClassXYZ> cxyz = new List<ClassXYZ>();

            Add<StructX>(SIZE, ref sx);
            Add<ClassX>(SIZE, ref cx);
            Add<StructXY>(SIZE, ref sxy);
            Add<ClassXY>(SIZE, ref cxy);
            Add<StructXYZ>(SIZE, ref sxyz);
            Add<ClassXYZ>(SIZE, ref cxyz);

            Call<StructX>(sx);
            Call<ClassX>(cx);
            Call<StructXY>(sxy);
            Call<ClassXY>(cxy);
            Call<StructXYZ>(sxyz);
            Call<ClassXYZ>(cxyz);

            // refで渡す場合
            // プロパティはrefで渡せないのでList<T>ではなくArrayを用意
            StructXYZ asxyz = new StructXYZ[SIZE];
            CallRef<StructXYZ>(ref asxyz);

            ClassXYZ acxyz = new ClassXYZ[SIZE];
            for (int i = 0; i < acxyz.Length; ++i)
                acxyz[i] = new ClassXYZ();
            CallRef<ClassXYZ>(ref acxyz);

            Console.ReadLine();
        }
    }
}
/* 結果
Add<StructX>      00:00:00.0468750
Add<ClassX>       00:00:04.2187500
Add<StructXY>     00:00:00.0625000
Add<ClassXY>      00:00:04.3750000
Add<StructXYZ>    00:00:00.0781250
Add<ClassXYZ>     00:00:04.5156250
Call<StructX>     00:00:00.3750000
Call<ClassX>      00:00:00.4687500
Call<StructXY>    00:00:01.9687500
Call<ClassXY>     00:00:00.4687500
Call<StructXYZ>   00:00:02.5781250
Call<ClassXYZ>    00:00:00.4687500
CallRef<StructXYZ>00:00:00.2187500
CallRef<ClassXYZ> 00:00:00.9531250
 */

このように、オブジェクトの生成は圧倒的にstructのパフォーマンスが良く、メソッドの引数では、サイズが4バイトまでなら辛うじてstructが勝ちますがそれ以上は、classの方が高速です。概ね言われている通りですね。ちなみにstructを参照渡し(ref)にするとclassよりも高速になりますが、参照渡しのメソッドはプロパティを取ることが出来ず、Listなどが使えなくなるのが痛いところです。