Выравнивание массивов в .NET - PullRequest
5 голосов
/ 16 марта 2012

Выровнены ли массивы в .NET по какой-либо границе?

Если да, то по какой?И одинаково ли это для всех типов массивов?

Ответы [ 4 ]

7 голосов
/ 16 марта 2012

Общая языковая инфраструктура (ECMA-335) накладывает следующие ограничения на выравнивание:

12.6.2 Выравнивание

Встроенные типы данных должны быть правильно выровнены, что определяется следующим образом:

  • 1-байтовые, 2-байтовые и 4-байтовые данные правильно выровнены, когда они сохраняются на 1-байтовой, 2-байтовой или 4-байтовой границе соответственно.
  • 8-байтовые данные правильно выровнены, когда они хранятся на той же границе, которая требуется базовому оборудованию для атомарного доступа к собственному int.

Таким образом, int16 и unsigned int16 начинаются с четного адреса; int32, unsigned int32 и float32 начинаются с адреса, кратного 4; и int64, unsigned int64 и float64 начинаются с адреса, кратного 4 или 8, в зависимости от целевой архитектуры. Типы собственных размеров (native int, native unsigned int и &) всегда выровнены естественным образом (4 байта или 8 байтов, в зависимости от архитектуры). При внешнем генерировании они также должны быть выровнены по своему естественному размеру, хотя переносимый код может использовать 8-байтовое выравнивание, чтобы гарантировать независимость архитектуры. Настоятельно рекомендуется выровнять float64 на 8-байтовой границе, даже если размер собственного int равен 32 битам.

CLI также указывает, что вы можете использовать префикс unaligned, чтобы разрешить произвольное выравнивание. Кроме того, JIT должен создавать правильный код для чтения и записи независимо от фактического выравнивания.

Кроме того, CLI допускает явное расположение полей класса:

  • explicitlayout: класс, помеченный explicitlayout, заставляет загрузчик игнорировать последовательность полей и использовать предоставленные явные правила компоновки в форме смещений полей и / или общего размера или выравнивания класса. Существуют ограничения на допустимые макеты, указанные в разделе II.

...

При желании разработчик может указать размер упаковки для класса. Это информация макета, которая используется не часто, но она позволяет разработчику контролировать выравнивание полей. Это не спецификация выравнивания как таковая, а скорее служит модификатором, который помещает потолок во все выравнивания. Типичные значения: 1, 2, 4, 8 или 16. Универсальные типы не должны маркироваться explicitlayout.

2 голосов
/ 16 марта 2012

Я сам этого не делал, но если вам нужно управлять выравниванием массива для совместимости с неуправляемым режимом, то вы можете рассмотреть возможность использования (небезопасного) фиксированного массива внутри структуры с применением StructLayoutAttributeи посмотрите, работает ли это.

1 голос
/ 06 ноября 2017

В .NET-объектах (из которых массивы являются разновидностями) всегда выравнивают на основе размера указателя (например, 4-байтовое или 8-байтовое выравнивание). Таким образом, указатели и массивы объектов всегда выровнены в .NET.

Код в ответе Михаэля Грачика проверяет выравнивание по индексу, потому что, хотя сам массив выровнен, так как это массив Int32, отдельные нечетные индексы не будут выровнены в 64-битных системах. В 32-битных системах все индексы массива Int32 будут выровнены.

Технически, этот метод мог бы быть быстрее, если бы он проверял битность процесса. В 32-битных процессах не нужно выполнять проверку выравнивания для массивов Int32. Поскольку все индексы будут выровнены по словам, а в этом случае указатели также имеют длину слова.

Следует также отметить, что разыменование указателя в .NET не требует выравнивания. Тем не менее, это будет медленнее. например если у вас есть действительный указатель байта *, который указывает на данные длиной не менее восьми байт, вы можете привести его к long * и получить значение:

unsafe
{
    var data = new byte[ 16 ];
    fixed ( byte* dataP = data )
    {
        var misalignedlongP = ( long* ) ( dataP + 3 );
        long value = *misalignedlongP;
    }
}

Читая исходный код .NET, вы можете видеть, что Microsoft иногда учитывает выравнивание, а часто - нет. Примером может служить внутренний метод System.Buffer.Memmove (см. https://referencesource.microsoft.com/#mscorlib/system/buffer.cs,c2ca91c0d34a8f86).. Этот метод имеет пути кода, которые преобразуют байт * в long без каких-либо проверок выравнивания в нескольких местах, и вызывающие методы также не проверяют выравнивание.

0 голосов
/ 18 июля 2012

Я не знаю об управляемых массивах, но в некоторых местах код BCL от Microsoft предполагает , что fixed массивы выровнены по словам. Вот пример из BitConverter.cs в .NET 4.0:

    public static unsafe int ToInt32 (byte[]value, int startIndex) { 
        //... Parameter validation

        fixed( byte * pbyte = &value[startIndex]) {
            if( startIndex % 4 == 0) { // data is aligned
                return *((int *) pbyte); 
            }
            else { 
               // .. do it the slow way
            } 
        }
    } 

Как видите, код проверяет выравнивание, используя startIndex, а не * pbyte. Есть только две причины, почему это так:

  1. pbyte всегда выровнен по словам.
  2. Это ошибка.

Я не думаю, что это ошибка. Я использую ToInt32 все время, и это никогда не вызывает у меня проблем. Я также склонен давать BCL преимущество сомнения, потому что авторы иногда имеют глубокие знания внутренних органов CLR.

Я думаю, можно с уверенностью предположить, что fixed массивы всегда выровнены по словам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...