Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

struct array vs object array c#

Tags:

arrays

c#

struct

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

like image 273
user2635088 Avatar asked Feb 26 '16 14:02

user2635088


1 Answers

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.

like image 164
Matthew Watson Avatar answered Oct 09 '22 06:10

Matthew Watson