Struct Layouts Performance Tip

Most developers aware of that Structs needed to be in smaller size (16byte or less) so they can run efficiently as possible. If you are interested in how the Struct size contributes to better performance, please refer to this question. This post is about another less popular tip that may contribute to improve the efficiency of Structs.
Just a bit of back ground on Layouts:

The way you structure your code, for example the order you specify fields within a type, is not necessarily the same way the CLR organize those fields. For performance reasons, CLR can re-arrange those fields of a type any way it chooses. Using StructureLayoutAttribute attribute, you can explicitly tell CLR the way you want to organize your fields. If you don’t specify a StructureLayoutAttribute, the compiler selects the most appropriate layout it thinks best.

For Structs, C# compiler creates the LayoutType.Sequnetial by default. This means the fields should stay in the order defined by the programmer.

(Note: For Reference types C# compiler selects the LayoutType.Auto by default.)
Below is a C# Struct with no explicit layout and the corresponding generated IL.

image 1

The Structs are commonly used when interoperating with unmanaged code. Therefore the fields must stay in the order that programmer define for this to work. But most user defined value types/Structs you create have nothing to do with interoperability with unmanaged code. Therefore if you specify the LayoutKind.Auto, the runtime automatically decide the appropriate layout.

 

image2

If you do a quick benchmark on both layouts you see the LayoutKind.Auto is much faster than the LayoutKind.Sequential.

image3

Result–

(Time in milliseconds)

image4

 

(Config: .NET 4, x86, and Release build)
If you want to look at the sample code is below.

//uses LayoutKind.Sequence by default
   2:    publicstructStructSeq

   privatereadonlyByte mb;
   privatereadonlyInt16 mx;
   publicstringa;
   publicstringb;
   publicstringc;
   publicstringd;
}
>
[StructLayout(LayoutKind.Auto)]
publicstructStructAuto
{
    privatereadonlyByte mb;
    privatereadonlyInt16 mx;
    publicstringa;
    publicstringb;
    publicstringc;
    publicstringd;
}

Program
{
  voidMain()
    {
        StructSeq sq = newStructSeq();
        Stopwatch sw1 = newStopwatch();
        sw1.Start();
        for(inti = 0; i < 8000000; i++)
        {
            ProcessStructSeq(refsq);
        }
        sw1.Stop();
        Console.WriteLine("Struct LayoutKind.Sequence (default) {0}", sw1.Elapsed.TotalMilliseconds);

        StructAuto so = newStructAuto();
        Stopwatch sw2 = newStopwatch();
        sw2.Start();
        for(inti = 0; i < 8000000; i++)
        {
            ProcessStructAuto(refso);
        }
        sw2.Stop();
        Console.WriteLine("Struct LayoutKind.Auto (explicit) {0}", sw2.Elapsed.TotalMilliseconds);

        Console.ReadLine();
    }

    publicstaticvoidProcessStructSeq(refStructSeq structSeq)
    {
        structSeq.a = "1";
        structSeq.b = "2";
        structSeq.c = "3";
        structSeq.d = "4";
    }

    publicstaticvoidProcessStructAuto(refStructAuto structAuto)
    {
        structAuto.a = "1";
        structAuto.b = "2";
        structAuto.c = "3";
        structAuto.d = "4";
    }
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s