Lamloumi Afif
Lamloumi Afif

Reputation: 9081

Marshall.sizeof and structure within C# application

I'd like to use the Marshall.SizeOf() method in my application

class Class1
{
    static void Main()
    {
        Console.WriteLine("Number of bytes needed by a PointB object: {0}",
      Marshal.SizeOf(typeof(PointB)));

        Console.WriteLine("Number of bytes needed by a PointA object: {0}",
          Marshal.SizeOf(typeof(PointA)));
        Console.ReadKey();
    }
    public struct PointA
    {
        public int x;
        public string posorneg;
        public bool isvalid;
    }
    public struct PointB
    {
        public bool isvalid;
        public int x;
        public string posorneg;

    }
}

I got as result :

Number of bytes needed by a PointB object: 16
Number of bytes needed by a PointA object: 24

I don't understand this result and I need to know :

  1. Does the order of the properties of a structure interfers in the number of bytes allocated?
  2. If it is the case, why? What are the best practises to reduce the number of bytes needed to allocate a structure?

Upvotes: 4

Views: 252

Answers (1)

Hans Passant
Hans Passant

Reputation: 941327

You are running this code in 64-bit mode, object references take 8 bytes. The default value of StructLayout.Pack is 8, matching the default setting for most C compilers. It ensures that members of a struct are aligned to an address that's a multiple of the member size. An important rule to make code fast and to keep updates to variables atomic. It avoids the processor having to use multiple memory bus cycles to access the variable value.

Annotating where every member is stored in the memory allocated for the struct:

    public struct PointA
    {
        public int x;             // Offset 0, 4 bytes
                                  // Offset 4, 4 bytes of padding
        public string posorneg;   // Offset 8, 8 bytes
        public bool isvalid;      // Offset 16, 4 bytes
                                  // Offset 20, 4 bytes of padding
    }                             // Total: 24 bytes
    public struct PointB
    {
        public bool isvalid;      // Offset 0, 4 bytes
        public int x;             // Offset 4, 4 bytes
        public string posorneg;   // Offset 8, 8 bytes
    }                             // Total: 16 bytes

The first 4 bytes of padding needed to be inserted to keep the string reference member aligned to 8. The last 4 bytes of padding where needed to ensure that the string is still aligned when you store the struct in an array.

No padding at all required in PointB, everything lined up correctly by accident. You can easily see the effect of the Pack attribute, apply [StructLayout(LayoutKind.Sequential, Pack = 4)] to the structs and you'll see they now both take 16 bytes. It is of course not actually possible in practice to reorder fields or tinker with the packing, you need to match the layout of the struct that the native code used.

Very notable is that the CLR performs this kind of layout optimization automatically. On a class or a struct with the [StructLayout(LayoutKind.Auto)] applied, it reorders fields to get the best layout. One of the niceties of managed code, making it competitive with native code. Do keep in mind that what you see from the Marshal class only applies after the class or struct is marshaled. The internal layout is undiscoverable, the reason that the CLR can play these optimization tricks.

Upvotes: 4

Related Questions