Каковы издержки памяти для типа пользовательской структуры .NET?

Есть фиксированные накладные расходы, связанные с объектом .NET, как более подробно описанные в этом вопросе SO: Что такое издержки памяти объекта .NET, равные 12 или 24 байта в зависимости от того, работаете ли вы в 32-битном или 64-битном процессе.

Тем не менее, базовые типы значений, такие как int, double, boolean и т.д. не имеют накладных расходов, потому что они являются типами значений.

Где это оставляет пользовательские типы struct, которые вы собрали вместе в своем приложении? С одной стороны, они представляют собой типы значений, такие как int, double, boolean выше [так что не должны налагать накладные расходы], но, с другой стороны, они получают косвенный характер из System.Object и поэтому должны (технически) налагать накладные расходы.

Ответ 1

Где это оставляет настраиваемые типы структур, которые вы собрали в ваше приложение?

Они ничем не отличаются от примитивных типов. Они не имеют дополнительных накладных расходов, кроме полей, которые у них есть. Тот факт, что они получены из object, не означает, что они несут накладные расходы, которые переносят ссылочные типы, который является указателем таблицы методов и блоком корня синхронизации.

Вы можете проверить это, используя Marshal.SizeOf:

void Main()
{
    var f = new Foo();
    Console.WriteLine(Marshal.SizeOf(f));
}

public struct Foo
{
    public int X { get; set; }
    public int Y { get; set; }
}

Это будет печатать 8 в 32-битном режиме, который представляет собой ровно два целочисленных значения (по 4 байта).

Примечание Marshal.SizeOf выведет размер неуправляемого объекта. CLR по-прежнему свободен для повторного упорядочения полей или их упаковки.

Ответ 2

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

Итак, для одного, структура не совсем маловероятна, чтобы содержать поле ссылочного типа. Как строка. В этом случае структура будет больше, поскольку ссылки являются указателями под капотом, вместо 8 - 8 байтов.

Прокладка - это очень тонкая деталь. В 32-битном режиме переменные не могут рассчитывать на выравнивание лучше, чем 4. Проблема с double и long, 8 байтовыми типами, которые легко могут быть ошибочно выровнены. В частности, затрагивая перформанс 32-битной программы, если двойной смещен по границе линии L1, то чтение или запись может быть 3 раза медленнее. Также основная причина, по которой эти типы не являются атомарными в модели памяти С#. Не проблема в 64-битном режиме, CLR тогда должна и обеспечивает гарантию выравнивания 8.

Тем не менее, CLR пытается дать правильное выравнивание таких элементов структуры в 32-битном режиме, даже если сама структура не будет выровнена. В противном случае это побочный эффект структур, имеющих неявный атрибут [StructLayout(LayoutKind.Sequential, Pack=8)]. Необычность в исходном коде CLR, оператор С++, который делает это, не имеет комментариев. Я подозреваю, что это быстрое решение для менее звездного неуправляемого interop perf, сохраняя структуру blittable очень важно для скорости.

Однако вы не всегда это понимаете, CLR отказывается, если структура содержит член, который сам является структурой, которая не имеет последовательного макета. В частности, это происходит для DateTime и DateTimeOffset, программисты, которые их написали, применили к ним атрибут [StructLayout (LayoutKind.Auto)] по очень загадочным причинам. В случае DateTimeOffset, вероятно, будет ошибка копирования/вставки. Макет вашей структуры теперь будет непредсказуемым, он также станет LayoutKind.Auto, и CLR повторно упорядочивает поля, чтобы минимизировать размер структуры. Это может вызвать дополнительное заполнение в режиме x64.

Это неясные детали реализации, о которых вы никогда не должны беспокоиться.

Ответ 3

У вас могут быть некоторые накладные расходы с выравниваниями полей:

https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute(v=vs.100).aspx

например.

  public struct Test {
    public Byte A;
    public long B;
  }

будет иметь размер 16 байт по 64-битовому процессу и 12 байтов по 32-битовому процессу соответственно (когда мы могли бы ожидать только 9 байтов);

Ответ 4

Ноль.

Если есть пробелы из-за выравнивания, в этом случае это затраты на эти пробелы.

Но в противном случае структура будет такой же, как если бы поля были отдельными переменными, выложенными в стеке.

Вставьте его, и вы справитесь с ним через object, который имеет те же накладные расходы, что и любой другой ссылочный тип.