简体   繁体   中英

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. Why is this so?

Secondly, when I compare the timings inside CompareTimesClassVsStruct() I get approximately the same time. 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.)

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 }; 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.

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.

This can be a problem with large arrays of large structs where the total size of the array would exceed 2^31 bytes.

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. 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.
  • However, if you had called an instance method on the result of the indexer, then a ref variable would have been produced for you; that ref variable is called "this", and it would have been passed to your instance method.

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.

In this case when I inspect the array after running the code, I get the mutated struct values. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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