Расположение последовательных структур CLR: выравнивание и размер - PullRequest
5 голосов
/ 25 ноября 2011

Все struct с в C # по умолчанию обрабатываются как [StructLayout(LayoutKind.Sequential)] помеченные типы значений. Итак, давайте возьмем некоторое количество struct с и проверим размеры этого struct с:

using System;
using System.Reflection;
using System.Linq;
using System.Runtime.InteropServices;

class Foo
{
  struct E { }
  struct S0 { byte a; }
  struct S1 { byte a; byte b; }
  struct S2 { byte a; byte b; byte c; }
  struct S3 { byte a; int b; }
  struct S4 { int a; byte b; }
  struct S5 { byte a; byte b; int c; }
  struct S6 { byte a; int b; byte c; }
  struct S7 { int a; byte b; int c; }
  struct S8 { byte a; short b; int c; }
  struct S9 { short a; byte b; int c; }
  struct S10 { long a; byte b; }
  struct S11 { byte a; long b; }
  struct S12 { byte a; byte b; short c; short d; long e; }
  struct S13 { E a; E b; }
  struct S14 { E a; E b; int c; }
  struct S15 { byte a; byte b; byte c; byte d; byte e; }
  struct S16 { S15 b; byte c; }
  struct S17 { long a; S15 b; }
  struct S18 { long a; S15 b; S15 c; }
  struct S19 { long a; S15 b; S15 c; E d; short e; }
  struct S20 { long a; S15 b; S15 c; short d; E e; }

  static void Main()
  {
    Console.WriteLine("name: contents => size\n");
    foreach (var type in typeof(Foo).GetNestedTypes(BindingFlags.NonPublic))
    {
      var fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
      Console.WriteLine("{0}: {2} => {1}", type.Name, Marshal.SizeOf(type),
        string.Join("+", fields.Select(_ => Marshal.SizeOf(_.FieldType))));
    }
  }
}

Вывод (то же самое для x86 / x64):

name: contents => size

E:  => 1
S0: 1 => 1
S1: 1+1 => 2
S2: 1+1+1 => 3
S3: 1+4 => 8
S4: 4+1 => 8
S5: 1+1+4 => 8
S6: 1+4+1 => 12
S7: 4+1+4 => 12
S8: 1+2+4 => 8
S9: 2+1+4 => 8
S10: 8+1 => 16
S11: 1+8 => 16
S12: 1+1+2+2+8 => 16
S13: 1+1 => 2
S14: 1+1+4 => 8
S15: 1+1+1+1+1 => 5
S16: 5+1 => 6
S17: 8+5 => 16
S18: 8+5+5 => 24
S19: 8+5+5+1+2 => 24
S20: 8+5+5+2+1 => 24

Глядя на эти результаты, я не могу понять CLR набора правил (выравнивание полей и общий размер), используемый для последовательных структур. Может кто-нибудь объяснить мне это поведение?

1 Ответ

12 голосов
/ 25 ноября 2011

Все поля выровнены в зависимости от их типа.Собственные типы (int, byte и т. Д.) Выровнены по размеру.Например, int всегда будет кратным 4 байтам, в то время как байт может быть где угодно.

Если поля меньшего размера предшествуют int, при необходимости будет добавлено заполнение, чтобы обеспечитьint правильно выровнен до 4 байтов.Вот почему S5 (1 + 1 + 4 = 8) и S8 (1 + 2 + 4 = 8) будут иметь отступы и будут иметь одинаковый размер:

[1][1][ ][ ][4] // S5
[1][ ][ 2  ][4] // S8

Кроме того,Сама struct наследует выравнивание своего наиболее выровненного поля (т. е. для S5 и S8, int является наиболее выровненным полем, поэтому оба они имеют выравнивание 4).Выравнивание наследуется таким образом, чтобы при наличии массива структур все поля во всех структурах были правильно выровнены.Таким образом, 4 + 2 = 8.

[4][2][ ][ ] // starts at 0
[4][2][ ][ ] // starts at 8
[4][2][ ][ ] // starts at 16

Обратите внимание, что 4 всегда выравнивается на 4. Без наследования от наиболее выровненного поля, каждый другой элемент в массиве будет иметь int, выровненный на 6байт вместо 4:

[4][2] // starts at 0
[4][2] // starts at 6 -- the [4] is not properly aligned!
[4][2] // starts at 12

Это было бы очень плохо, потому что не все архитектуры допускают чтение с невыровненных адресов памяти, и даже те, которые имеют (потенциально довольно большой, если на строке или странице кэша)ограничение производительности за это.

Помимо базовой производительности, выравнивание также вступает в игру с параллелизмом.Модель памяти C # гарантирует, что чтение / запись нативных типов шириной до 4 байтов являются атомарными, а .NET имеет атомарные функции, такие как класс Interlocked.Атомарные операции, подобные этим, сводятся к инструкциям процессора, которые сами по себе требуют выровненного доступа к памяти для работы.

Правильное выравнивание очень важно!

Вы часто будете видеть, как умные нативные кодировщики помнят все это, покавыкладывая их структуры, сортируя все поля от самых больших до самых маленьких, стараясь свести к минимуму заполнение и, следовательно, размер структуры.

...