简体   繁体   English

struct array vs object array c#

[英]struct array vs object array c#

I understand that mutable structs are evil. 我知道可变结构是邪恶的。 However, I'd still like to compare the performance of an array of structs vs an array of objects. 但是,我仍然想比较一个结构数组与一个对象数组的性能。 This is what I have so far 这就是我到目前为止所拥有的

 public struct HelloStruct
    {
        public int[] hello1;
        public int[] hello2;
        public int hello3;
        public int hello4;
        public byte[] hello5;
        public byte[] hello6;
        public string hello7;
        public string hello8;
        public string hello9;
        public SomeOtherStruct[] hello10;

    }

    public struct SomeOtherStruct
    {
        public int yoyo;
        public int yiggityyo;
    }

    public class HelloClass
    {
        public int[] hello1;
        public int[] hello2;
        public int hello3;
        public int hello4;
        public byte[] hello5;
        public byte[] hello6;
        public string hello7;
        public string hello8;
        public string hello9;
        public SomeOtherClass[] hello10;

    }
        public class SomeOtherClass
    {
        public int yoyo;
        public int yiggityyo;
    }

 static void compareTimesClassVsStruct()
    {
        HelloStruct[] a = new HelloStruct[50];
        for (int i = 0; i < a.Length; i++)
        {
            a[i] = default(HelloStruct);
        }

        HelloClass[] b = new HelloClass[50];
        for (int i = 0; i < b.Length; i++)
        {
            b[i] = new HelloClass();
        }
        Console.WriteLine("Starting now");
        var s1 = Stopwatch.StartNew();
        for (int i = 0; i < _max; i++)
        {
            a[i % 50].hello1 = new int[] { 1, 2, 3, 4, i % 50 };
            a[i % 50].hello3 = i;
            a[i % 50].hello7 = (i % 100).ToString();
        }
        s1.Stop();

        var s2 = Stopwatch.StartNew();
        for (int j = 0; j < _max; j++)
        {
            b[j % 50].hello1 = new int[] { 1, 2, 3, 4, j % 50 };
            b[j % 50].hello3 = j;
            b[j % 50].hello7 = (j % 100).ToString();
        }
        s2.Stop();

        Console.WriteLine(((double)(s1.Elapsed.TotalSeconds)));
        Console.WriteLine(((double)(s2.Elapsed.TotalSeconds)));
        Console.Read();

    }

There's a couple of things happening here that I'd like to understand. 这里发生了一些我想要了解的事情。

Firstly, since the array stores structs, when I try to access a struct from the array using the index operation, should I get a copy of the struct or a reference to the original struct? 首先,由于数组存储结构,当我尝试使用索引操作从数组访问结构时,我应该获得结构的副本还是对原始结构的引用? In this case when I inspect the array after running the code, I get the mutated struct values. 在这种情况下,当我在运行代码后检查数组时,我得到了变异的struct值。 Why is this so? 为什么会这样?

Secondly, when I compare the timings inside CompareTimesClassVsStruct() I get approximately the same time. 其次,当我比较CompareTimesClassVsStruct()的时间时,我得到大致相同的时间。 What is the reason behind that? 这背后的原因是什么? Is there any case under which using an array of structs or an array of objects would outperform the other? 是否有任何情况下使用结构数组或对象数组会胜过另一个?

Thanks 谢谢

When you access the properties of an element of an array of structs, you are NOT operating on a copy of the struct - you are operating on the struct itself. 当您访问结构数组元素的属性时,您不是在结构的副本上操作 - 您正在对结构本身进行操作。 (This is NOT true of a List<SomeStruct> where you will be operating on copies, and the code in your example wouldn't even compile.) (对于您将在副本上操作的List<SomeStruct> ,情况并非如此,您的示例中的代码甚至无法编译。)

The reason you are seeing similar times is because the times are being distorted by the (j % 100).ToString() and new int[] { 1, 2, 3, 4, j % 50 }; 你看到相似时间的原因是因为时间被(j % 100).ToString()new int[] { 1, 2, 3, 4, j % 50 };扭曲了new int[] { 1, 2, 3, 4, j % 50 }; within the loops. 循环内。 The amount of time taken by those two statements is dwarfing the times taken by the array element access. 这两个语句所花费的时间使数组元素访问所花费的时间相形见绌。

I changed the test app a little, and I get times for accessing the struct array of 9.3s and the class array of 10s (for 1,000,000,000 loops), so the struct array is noticeably faster, but pretty insignificantly so. 我稍微更改了测试应用程序,并且我有时间访问9.3s的struct数组和10s的类数组(对于1,000,000,000个循环),因此struct数组明显更快,但非常微不足道。

One thing which can make struct arrays faster to iterate over is locality of reference. 可以使结构数组更快迭代的一件事是引用的局部性。 When iterating over a struct array, adjacent elements are adjacent in memory, which reduces the number of processor cache misses. 迭代结构数组时,相邻元素在内存中相邻,这减少了处理器缓存未命中数。

The elements of class arrays are not adjacent (although the references to the elements in the array are, of course), which can result in many more processor cache misses while you iterate over the array. 类数组的元素不相邻(当然,虽然引用了数组中的元素),但是当您遍历数组时,这会导致更多的处理器缓存未命中。

Another thing to be aware of is that the number of contiguous bytes in a struct array is effectively (number of elements) * (sizeof(element)) , whereas the number of contiguous bytes in a class array is (number of elements) * (sizeof(reference)) where the size of a reference is 32 bits or 64 bits, depending on memory model. 另一件需要注意的事情是,struct数组中连续字节的数量是有效的(number of elements) * (sizeof(element)) ,而类数组中的连续字节(number of elements) * (sizeof(reference))(number of elements) * (sizeof(reference))其中(number of elements) * (sizeof(reference))的大小是32位或64位,具体取决于内存模型。

This can be a problem with large arrays of large structs where the total size of the array would exceed 2^31 bytes. 这可能是大型结构数组的问题,其中数组的总大小将超过2 ^ 31个字节。

Another difference you might see in speed is when passing large structs as parameters - obviously it will be much quicker to pass by value a copy of the reference to a reference type on the stack than to pass by value a copy of a large struct. 您可能在速度上看到的另一个区别是将大型结构作为参数传递 - 显然,将值的引用副本传递给堆栈上的引用类型比通过值传递大型结构的副本要快得多。

Finally, note that your sample struct is not very representative. 最后,请注意您的示例结构不是很有代表性。 It contains a lot of reference types, all of which will be stored somewhere on the heap, not in the array itself. 它包含许多引用类型,所有引用类型都将存储在堆上的某个位置,而不是存储在数组本身中。

As a rule of thumb, structs should not be more than 32 bytes or so in size (the exact limit is a matter of debate), they should contain only primitive (blittable) types, and they should be immutable. 根据经验,结构的大小不应超过32个字节(确切的限制是争论的问题),它们应该只包含原始(blittable)类型,它们应该是不可变的。 And, usually, you shouldn't worry about making things structs anyway, unless you have a provable performance need for them. 而且,通常情况下,你不应该担心结构的结构,除非你有一个可证明的性能需求。

Firstly, since the array stores structs, when I try to access a struct from the array using the index operation, should I get a copy of the struct or a reference to the original struct? 首先,由于数组存储结构,当我尝试使用索引操作从数组访问结构时,我应该获得结构的副本还是对原始结构的引用?

Let me tell you what is actually happening rather than answering your confusingly worded either-or question. 让我告诉你实际发生了什么,而不是回答你那令人困惑的措辞或问题。

  • Arrays are a collection of variables . 数组是变量的集合。
  • The index operation when applied to an array produces a variable . 应用于数组时的索引操作会生成变量
  • Mutating a field of a mutable struct successfully requires that you have in hand the variable that contains the struct you wish to mutate. 成功地改变可变结构的字段需要您拥有包含要变异的结构的变量

So now to your question: Should you get a reference to the struct? 那么现在问你的问题:你应该获得对结构的引用吗?

  • Yes, in the sense that a variable refers to storage . 是的,从某种意义上说,变量是指存储
  • No, in the sense that the variable does not contain a reference to an object ; 不,从某种意义上说,变量不包含对象的引用 ; the struct is not boxed. 结构不是盒装的。
  • No, in the sense that the variable is not a ref variable. 不,从某种意义上说,变量不是ref变量。
  • However, if you had called an instance method on the result of the indexer, then a ref variable would have been produced for you; 但是,如果你在索引器的结果上调用了一个实例方法,那么就会为你生成一个ref变量; that ref variable is called "this", and it would have been passed to your instance method. ref变量称为“this”,它将被传递给您的实例方法。

You see how confusing this gets. 你看到这有多混乱。 Better to not think about references at all. 最好不要考虑参考文献。 Think about variables and values . 考虑变量 Indexing an array produces a variable . 索引数组会生成一个变量

Now deduce what would have happened had you used a list rather than an array, knowing that the getter indexer of a list produces a value, not a variable. 现在推断出使用列表而不是数组会发生什么,知道列表的getter索引器产生的是值,而不是变量。

In this case when I inspect the array after running the code, I get the mutated struct values. 在这种情况下,当我在运行代码后检查数组时,我得到了变异的struct值。 Why is this so? 为什么会这样?

You mutated a variable. 你改变了一个变量。

I get approximately the same time. 我大约在同一时间。 What is the reason behind that? 这背后的原因是什么?

The difference is so tiny that it is being swamped by all the memory allocations and memory copies you are doing in both cases. 差异非常小,以至于在两种情况下都会被所有内存分配和内存副本所淹没。 That is the real takeaway here. 这是真正的外卖。 Are operations on mutable value types stored in arrays slightly faster? 对存储在数组中的可变值类型的操作是否稍快一些? Possibly. 有可能。 (They save on collection pressure as well, which is often the more relevant performance metric.) But though the relative savings might be significant, the savings as a percentage of total work is often tiny. (它们也节省了收集压力,这通常是更相关的性能指标。)但是,虽然相对节省可能很大,但节省占总工作的百分比通常很小。 If you have a performance problem then you want to attack the most expensive thing, not something that is already cheap. 如果你遇到性能问题,那么你想要攻击最昂贵的东西,而不是那些已经很便宜的东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM