为什么结构比类慢?

时间:2016-12-01 16:15:18

标签: c# struct

我正在创建一个C#console-app。我有一些关键路径,并认为创建结构比创建类更快,因为我不需要对结构进行垃圾回收。然而,在我的测试中,我找到了相反的结果。

在下面的测试中,我创建了1000个结构和1000个类。

class Program
{
    static void Main(string[] args)
    {
        int iterations = 1000;
        Stopwatch sw = new Stopwatch();

        sw.Start();
        List<Struct22> structures = new List<Struct22>();
        for (int i = 0; i < iterations; ++i)
        {
            structures.Add(new Struct22());
        }
        sw.Stop();
        Console.WriteLine($"Struct creation consumed {sw.ElapsedTicks} ticks");

        Stopwatch sw2 = new Stopwatch();
        sw2.Start();
        List<Class33> classes = new List<Class33>();
        for (int i = 0; i < iterations; ++i)
        {
            classes.Add(new Class33());
        }
        sw2.Stop();
        Console.WriteLine($"Class creation consumed {sw2.ElapsedTicks} ticks");


        Console.ReadLine();
    }
}

我的classe / struct很简单:

class Class33
{
    public int Property { get; set; }
    public int Field;
    public void Method() { }
}

struct Struct22
{
    public int Property { get; set; }
    public int Field;
    public void Method() { }
}

结果(鼓请......)

Struct creating consuming 3038 ticks
Class creating consuming 404 ticks

所以问题是:为什么一个Class的时间量比结束的时间接近10倍?

EDIT。我做了这个计划&#34;做点什么&#34;只需将属性分配给属性即可。

 static void Main(string[] args)
    {
        int iterations = 10000000;
        Stopwatch sw = new Stopwatch();

        sw.Start();
        List<Struct22> structures = new List<Struct22>();
        for (int i = 0; i < iterations; ++i)
        {
            Struct22 s = new Struct22()
            {
                Property = 2,
                Field = 3
            };
            structures.Add(s);
        }
        sw.Stop();
        Console.WriteLine($"Struct creating consuming {sw.ElapsedTicks} ticks");

        Stopwatch sw2 = new Stopwatch();
        sw2.Start();
        List<Class33> classes = new List<Class33>();
        for (int i = 0; i < iterations; ++i)
        {
            Class33 c = new Class33()
            {
                Property = 2,
                Field = 3
            };
            classes.Add(c);
        }
        sw2.Stop();
        Console.WriteLine($"Class creating consuming {sw2.ElapsedTicks} ticks");


        Console.ReadLine();
    }

结果令我震惊。类仍然是至少2倍,但分配整数的简单事实有20倍的影响!

Struct creating consuming 903456 ticks
Class creating consuming 4345929 ticks

编辑:我删除了对方法的引用,因此我的类或结构中没有引用类型:

class Class33
{
    public int Property { get; set; }
    public int Field;
}

struct Struct22
{
    public int Property { get; set; }
    public int Field;
}

3 个答案:

答案 0 :(得分:1)

性能差异可能(或至少部分)可以通过一个简单的例子来解释。

对于structures.Add(new Struct22());,这才是真正发生的事情:

  • 创建Struct22并初始化。
  • 调用Add方法,但会收到副本,因为该项目是值类型

因此,在这种情况下调用Add会产生开销,这是通过创建一个新的Struct22并从原始文件中复制所有字段和属性而产生的。


为了证明,不是关注速度,而是关注复制的事实:

private static void StructDemo()
{
    List<Struct22> list = new List<Struct22>();

    Struct22 s1 = new Struct22() { Property = 2, Field = 3 };  // #1
    list.Add(s1);                            // This creates copy #2
    Struct22 s3 = list[0];                   // This creates copy #3

    // Change properties:
    s1.Property = 777;
    // list[0].Property = 888;    <-- Compile error, NOT possible
    s3.Property = 999;

    Console.WriteLine("s1.Property = " + s1.Property);
    Console.WriteLine("list[0].Property = " + list[0].Property);
    Console.WriteLine("s3.Property = " + s3.Property);
}

这将是输出,证明Add()和使用list[0]都会导致复制:

  

s1.Property = 777
  list [0] .Property = 2
  s3.Property = 999

让这提醒一下,与对象相比,结构体的行为可能会有很大的不同,在决定使用什么时,性能应该只是一个方面。

答案 1 :(得分:0)

如评论所述,决定struct vs class有很多考虑因素。我没有看到很多人关注实例化,因为它通常只是基于这种决定的性能影响的一小部分。

我用你的代码运行了一些测试,发现有趣的是随着实例数量的增加,结构更快。

我无法回答你的问题,因为看来你的断言不正确。类并不总是比Structs更快地实例化。我读过的所有内容都反过来说,但是你的测试会产生你提到的有趣结果。

您可以使用一些工具来深入挖掘并尝试找出您获得结果的原因。

10000 
Struct creation consumed 2333 ticks
Class creation consumed 1616 ticks

100000
Struct creation consumed 5672 ticks
Class creation consumed 8459 ticks

1000000
Struct creation consumed 73462 ticks
Class creation consumed 221704 ticks

答案 2 :(得分:0)

List<T>将T对象存储在内部数组中。

每次达到容量限制时,都会创建新的双倍大小的内部数组,并复制旧数组中的所有值...

当您创建一个空列表并尝试填充1000次时,内部数组会重新创建并复制约10次。

因此,在您的示例中,类可能创建得更慢,但每次创建新数组时,List应仅复制对类的List的对象的引用,以及结构列表的所有结构数据...

尝试创建初始容量初始化的List,代码应为:

new List<Struct22>(1000)

在这种情况下,内部数组不会被重新创建,结构案例将更快地运行