Что означает «Выравнивание байтов в Packed Now Forces»? - PullRequest
21 голосов
/ 11 декабря 2011

Что нового в Delphi XE2 содержит после .

Packed Now принудительно выравнивает байты записей

Если у вас есть устаревший код, который использует упакованный тип записи, и вы хотите чтобы связать с внешней DLL или с C ++, вам нужно удалить слово "упакован" из вашего кода. Теперь упакованное ключевое слово вызывает выравнивание байтов, тогда как в прошлом это не обязательно делало это. Поведение изменение связано с изменениями совместимости выравнивания C ++ в Delphi 2009

Я не понимаю этого. Я борюсь с этим пунктом: , тогда как в прошлом это не обязательно делало это . То, что я не могу смириться, так это то, что упакованное всегда приводило к выравниванию байтов записей, насколько мне известно. Кто-нибудь может привести пример упакованной записи, которая не выровнена по байту? Очевидно, это должно быть в более ранней версии.

Почему в документах говорится: «Если вы хотите связать с внешней DLL или с C ++, вам нужно удалить слово, упакованное в вашем коде»? Если во внешнем коде используется #pragma pack(1), что нам делать, если упакованный пакет недоступен?

А как насчет директивы $ALIGN? * {$A1} and {$A-} эквивалентно packed или есть какое-то дополнительное значение с packed?

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

Обновление

Я разумно убежден, что документация относится к выравниванию самой записи, а не к макету записи. Вот небольшая программа, которая показывает, что использование packed для записи заставляет выравнивание записи равным 1.

program PackedRecords;
{$APPTYPE CONSOLE}
type
  TPackedRecord = packed record
    I: Int64;
  end;

  TPackedContainer = record
    B: Byte;
    R: TPackedRecord;
  end;

  TRecord = record
    I: Int64;
  end;

  TContainer = record
    B: Byte;
    R: TRecord;
  end;

var
  pc: TPackedContainer;
  c: TContainer;

begin
  Writeln(NativeInt(@pc.R)-NativeInt(@pc.B));//outputs 1
  Writeln(NativeInt(@c.R)-NativeInt(@c.B));//outputs 8
  Readln;
end.

Это приводит к тому же выводу в Delphi 6, 2010, XE и XE2 32-битных и XE 64-битных.

Ответы [ 4 ]

5 голосов
/ 05 апреля 2013

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

2 голосов
/ 12 декабря 2011

Поскольку я не являюсь компилятором Delphi, я также могу догадаться, как и многие другие: выравнивание записи может быть предназначено не для выравнивания элементов внутри записи, а для самой записи.

Если вы объявляете переменную записи, она выравнивается по некоторому адресу в памяти, который, скорее всего, выравнивается по 4-байтовой границе. Это имело место (как проверено в D2007) для упакованных и распакованных записей.

Теперь в XE2 упакованная запись помещается в 1-байтовую границу, в то время как неупакованные записи размещаются на некоторой четной границе, которой можно управлять с помощью ключевого слова align. Как это:

type
  TRecAligned = record
    b1: byte;
    u1: uint64;
  end align 16;

  TRecPackedAligned = packed record
    b1: byte;
    u1: uint64;
  end align 16;

Упакованная запись по-прежнему выравнивается на границе в 1 байт, а неупакованная запись - на границе 16 байтов.

Как я уже сказал, это только предположение. Формулировка цитаты Embarcadero не очень ясна на предмете.

1 голос
/ 11 декабря 2011

Насколько я помню, record раньше был упакован начиная с версии компилятора вокруг Delphi 5-6.

Затем, по соображениям производительности, простые record поля были выровнены, согласнонастройки в опции проекта.Если вы определите packed record, то в записи не будет никакого выравнивания.

Цитируемый текст, похоже, относится не только к XE2, но и к изменению Delphi 2009.См. эту запись в блоге для исторических целей.

Я предполагаю, что " 'Packed' Now Forces Byte Alignment of Records " относится к функции Delphi 2009 {$OLDTYPELAYOUT ON} -который может иметь некоторые проблемы с реализацией до XE2.Я согласен с вами: это звучит как проблема с документацией.

0 голосов
/ 11 декабря 2011

Паскаль всегда поддерживал упакованные структуры, которые, вероятно, проще всего объяснить на примере

Допустим, у вас есть два массива x и y и что мы используем 16-битный процессор. То есть размер слова процессора составляет 16 бит.

myrec = 
record
  b : byte;
  i : integer;
end;

x : array[1..10] of myrec;
y : packed array[1..10] of myrec;

Паскаль только говорит вам, что у вас есть 10 элементов для работы, каждый из которых содержит 3 байта информации (допустим, целое число - это старое 16-битное разнообразие). Это фактически ничего не говорит о том, как эта информация хранится. Вы можете предположить, что массив хранится в 30 последовательных байтах, однако это не обязательно верно и полностью зависит от компилятора (действительно веская причина избегать математики указателей).

Компилятор вполне может поместить фиктивный байт между значениями поля b и i, чтобы гарантировать, что и b, и i попадают на границы слов. В этом случае структура займет всего 40 байтов.

Зарезервированное слово «упаковано» указывает компилятору оптимизировать размер по скорости, тогда как, оставляя упакованным, компилятор обычно оптимизирует скорость по размеру. В этом случае структура будет оптимизирована и займет всего 30 байт.

Снова я говорю «мог», потому что он все еще зависит от компилятора. Ключевое слово «упаковано» просто говорит, что нужно упаковать данные ближе друг к другу, но на самом деле не говорит как. Поэтому некоторые компиляторы просто игнорировали ключевое слово, тогда как другие использовали упакованные, чтобы отключить выравнивание слов.

В последнем случае, как правило, может происходить то, что каждый элемент выравнивается по размеру слова процессора. Здесь вы должны понимать, что размер слова процессора - это, как правило, размер регистра, а не просто «16 бит», как это обычно происходит. Например, в случае 32-битного процессора размер слова составляет 32 бита (хотя мы в общем определяем слова как 16-битные - это просто исторический возврат) В некотором смысле это «эквивалент памяти» сектора диска.

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

В общем, что означает изменение, так это то, что delphi теперь полностью использует упакованное ключевое слово и постоянно выполняет выравнивание байтов (по выравниванию слов).

Надеюсь, этого достаточно, чтобы объяснить это. На самом деле это большая тема, на которую я могу разумно ответить в нескольких параграфах.


ОБНОВЛЕНИЕ, продолжение цепочки комментариев ниже ...

DavidH> Я не могу представить, что вы имеете в виду, что компилятор способен выдавать различный вывод при одинаковом вводе

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

TonyH> Я думаю, что большинство из нас всегда предполагало, что упакованный = байт выровнен, поэтому сейчас мы ломаем голову над сценарием, где это не так. @ Дэвид Хеффернан спрашивает, знает ли кто-нибудь один

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

Я могу привести множество примеров. Допустим, у вас есть следующее (я использую массив, так же легко можно применить к записи)

myarray = packed array[1..8] of boolean

Если вы затем распечатаете SizeOf (myarray), что вы ожидаете? Ответ заключается в том, что Delphi не гарантирует, что упакованный реализован каким-либо конкретным способом. Поэтому ответ может быть 8 байтов (если вы выравниваете байты каждого элемента). Delphi также может упаковывать их как биты, поэтому весь массив может уместиться в 1 байт.Это может быть 16 байт, если компилятор решит не оптимизировать границы байтов и работает на 16-битной архитектуре. Да, это было бы менее эффективно с точки зрения скорости, но весь смысл упаковки заключается в том, чтобы оптимизировать размер по скорости. Независимо от того, что он делает, он невидим для разработчика, если он просто обращается к элементам массива и не делает никаких предположений и пытается использовать собственную математику указателей для доступа к базовым данным.

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

Эти типы архитектурных проблем как раз и объясняют, почему были разработаны различные методы, такие как файл с разделителями-запятыми, xml и т. Д. Чтобы обеспечить надежную общую платформу.

Подводя итог, Дэвид, проблема в том, что вы на самом деле задаете не тот вопрос. Если вы рассмотрите цитату Эмбаркадеро в контексте того, что я сказал, вы увидите, что она соответствует тому, что я говорю.

...