【C#】インスタンス作成速度の比較

C# で数通りあるインスタンス作成方法の処理速度の比較です。

前提条件・環境

環境は以下の通り

  • Visual Studio 2019 + .NET Core 3.1
  • Windows10, Releaseビルドをコンソールから実行

動作検証

検証は BenchmarkDotNet を使用して検証は以下の通り実行します。

// Program.cs

internal class Program
{
    private static void Main(string[] args)
    {
        var summary = BenchmarkRunner.Run<CreateInstance1>();
        // もしくは以下を使う
        //var summary = BenchmarkRunner.Run<CreateInstance2>();
    }
}

試験に使用するクラスの定義は以下の通りです。

// MyClass.cs

public class MyClass
{
    string _str;
    int _value;
    public MyClass() { }
    public MyClass(string str, int value) => (_str, _value) = (str, value);
    public override string ToString() => $"{_str}={_value}";
}

引数無しのインスタンス作成

引数無しの場合以下の方法でインスタンスを作成できます。

  • Case1: newして作成
  • Case2: ジェネリックで作成
  • Case3: Activatorで作成(ジェネリックを使用)
  • Case4: Activatorで作成(Type渡しを使用)

検証コード

[MemoryDiagnoser]
[RankColumn]
public class CreateInstance1
{
    [Benchmark]
    public void Case1()
    {
        _list1.Clear();
        for (int i = 0; i < 1000; i++)
        {
            var obj = new MyClass();
            _list1.Add(obj);
        }
    }
    List<MyClass> _list1 = new List<MyClass>();

    [Benchmark]
    public void Case2()
    {
        _list2.Clear();
        for (int i = 0; i < 1000; i++)
        {
            var obj = Create<MyClass>();
            _list2.Add(obj);
        }
    }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static T Create<T>() where T : class, new() => new T();
    List<MyClass> _list2 = new List<MyClass>();

    [Benchmark]
    public void Case3()
    {
        _list3.Clear();
        for (int i = 0; i < 1000; i++)
        {
            var obj = Activator.CreateInstance<MyClass>();
            _list3.Add(obj);
        }
    }
    List<MyClass> _list3 = new List<MyClass>();

    [Benchmark]
    public void Case4()
    {
        _list4.Clear();
        for (int i = 0; i < 1000; i++)
        {
            var obj = (MyClass)Activator.CreateInstance(_type);
            _list4.Add(obj);
        }
    }
    Type _type = typeof(MyClass);
    List<MyClass> _list4 = new List<MyClass>();
}

実行結果

| Method |      Mean |     Error |    StdDev | Rank |   Gen0 |   Gen1 | Allocated |
|------- |----------:|----------:|----------:|-----:|-------:|-------:|----------:|
|  Case1 |  5.246 us | 0.0726 us | 0.0607 us |    1 | 7.6447 | 0.0076 |  31.25 KB |
|  Case2 | 31.587 us | 0.2984 us | 0.2646 us |    2 | 7.6294 |      - |  31.25 KB |
|  Case3 | 31.233 us | 0.4671 us | 0.4369 us |    2 | 7.6294 |      - |  31.25 KB |
|  Case4 | 33.266 us | 0.5065 us | 0.4737 us |    3 | 7.6294 | 0.0305 |  31.25 KB |
  • Case2~4 Case1に比べて6倍遅い
  • Case4 は Case2,3 に比べて微妙に遅い

引数ありのインスタンス作成

引数ありの場合はジェネリックが使用できないので以下の 2通りのインスタンス作成方法があります。

  • newして作成
  • Activatorで作成

検証コード

[MemoryDiagnoser]
[RankColumn]
public class CreateInstance2
{
    [Benchmark]
    public void Case1()
    {
        _list1.Clear();
        string str = "asdf";
        int value = 100;
        for (int i = 0; i < 1000; i++)
        {
            var obj = new MyClass(str, value);
            _list1.Add(obj);
        }
    }
    List<MyClass> _list1 = new List<MyClass>();

    [Benchmark]
    public void Case2()
    {
        _list2.Clear();
        string str = "asdf";
        int value = 100;
        for (int i = 0; i < 1000; i++)
        {
            var obj = (MyClass)Activator.CreateInstance(_type, str, value);
            _list2.Add(obj);
        }
    }
    Type _type = typeof(MyClass);
    List<MyClass> _list2 = new List<MyClass>();
}

実行結果

| Method |       Mean |     Error |    StdDev | Rank |    Gen0 |   Gen1 | Allocated |
|------- |-----------:|----------:|----------:|-----:|--------:|-------:|----------:|
|  Case1 |   6.012 us | 0.1196 us | 0.2575 us |    1 |  1.9073 | 0.0992 |  31.25 KB |
|  Case2 | 311.745 us | 5.9345 us | 6.5962 us |    2 | 26.3672 | 1.4648 |  437.5 KB |
  • Case1 と 2で50倍以上速度差がある

かなり昔から Activator.CreateInstanceは 遅いと言われていました、実際かなり遅いので通常は使用することはないと思います。

引数無しのほうはまぁまぁの速度でしたが引数ありの CreateInstance はかなり厳しいですね。

なぜか Allocated の項目が引数なしに比べて 15倍も増えてます。