Delphi Array Alignment установлен на 4, 8 или 16 байтовых границ? - PullRequest
2 голосов
/ 11 мая 2009

Я хотел бы использовать библиотеку FFTW C из Delphi 2009 и в соответствии с этой документацией;

http://www.fftw.org/install/fftw_usage_from_delphi.txt

для повышения производительности в библиотеке FFTW (так, чтобы она могла использовать расширения SIMD) массивы, переданные в Single (float) или Double (double), должны быть выровнены либо на границах 4 или 8 байтов. Я нашел документацию, рассказывающую о выравнивании структур записи, но ничего конкретного о массивах. Есть ли способ сделать это в Delphi 2009.

Таким образом, код (скопированный из вышеприведенной документации) будет выглядеть так:

var
      in, out : Array of Single; // Array aligned at 4 byte boundary
      plan : Pointer;

    {$APPTYPE CONSOLE}

    begin

      ...  

      SetLength(in, N);
      SetLength(out, N);

      plan := _fftwf_plan_dft_1d(dataLength, @in[0], @out[0],
                                 FFTW_FORWARD, FFTW_ESTIMATE);

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

Спасибо, Bruce

Ответы [ 4 ]

6 голосов
/ 11 мая 2009

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

procedure TForm1.Button1Click(Sender: TObject);
type
  TFFTData = array[0..63535] of double;
  PFFTData = ^TFFTData;
var
  Buffer: pointer;
  FFTDataPtr: PFFTData;
  i: integer;
const
  Alignment = 128; // needs to be power of 2
begin
  GetMem(Buffer, SizeOf(TFFTData) + Alignment);
  try
    FFTDataPtr := PFFTData((LongWord(Buffer) + Alignment - 1)
                           and not (Alignment - 1));

    // use data...
    for i := Low(TFFTData) to High(TFFTData) do
      FFTDataPtr[i] := i * pi;

  finally
    FreeMem(Buffer);
  end;
end;

Edit:

Относительно комментария о двойном выделении памяти: переменная стека FFTData имеет тип PFFTData, а не TFFTData, так что это указатель. Это не так очевидно из-за улучшения синтаксиса, позволяющего опускать ^ для разыменования указателя. Память выделяется с помощью GetMem (), и для работы с правильным типом вместо нетипизированного блока памяти используется типовое преобразование. Наверное, мне следовало бы назвать его FFTDataPtr.

3 голосов
/ 11 мая 2009

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

Если вы обеспокоены тем, что диспетчер памяти Delphi не обеспечивает желаемого выравнивания для динамических массивов, тогда вы можете пойти дальше и использовать функции памяти, предоставляемые DLL. В цитируемой вами заметке упоминаются _fftwf_malloc и _fftwf_free, но затем она дает какое-то предупреждение о том, что память, выделенная из _fftwf_malloc, "не может быть доступна напрямую из Delphi". Это не может быть тем, что хотели сказать авторы, потому что это не то, как работает память в Windows. Авторы, вероятно, хотели сказать, что память, выделенная _fftwf_malloc, не может быть освобождена Delphi FreeMem, а память, выделенная Delphi GetMem, не может быть освобождена _fftwf_free. В этом нет ничего особенного; вам всегда необходимо поддерживать сопряжение функций управления памятью.

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

var
  dataIn, dataOut: PDouble;
begin
  dataIn := _fftwf_malloc(...);
  dataOut := _fftwf_malloc(...);
  _fftwf_plan_dft_1d(dataLength, dataIn, dataOut,
                     FFTW_FORWARD, FFTW_ESTIMATE);

Начиная с Delphi 2009, вы даже можете использовать синтаксис массива для этих указателей:

dataIn[0] := 3.5;
dataIn[2] := 7.3;

Чтобы включить это, используйте директиву компилятора {$POINTERMATH ON}; он не включен по умолчанию, за исключением типов символьных указателей.

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

2 голосов
/ 11 мая 2009

Блоки кучи iirc всегда выровнены по 16-байтовым границам FastMM (старый mem7ager D7 выровнен по 8). Я не знаю о sharemem, так как я им не пользуюсь.

А динамические массивы - это структуры на основе кучи. Массивы OTOH dyn могут стать невыровненными (от 16 до 8), потому что есть префикс длины и количества ссылок. Проще всего просто напечатать

ptruint (@in [0]) в шестнадцатеричном виде и посмотрите, будет ли конец 0 или 8. (*)

Обратите внимание, что в FPC есть заголовки fftw. (packages / fftw), afaik недавно была исправлена ​​даже для 64-битной версии.

Мне не известны директивы выравнивания стека в Delphi. Может быть, они автоматически «естественно» выровнены, хотя.

(*) ptruint - это FPC, говорящий для целого типа без знака, который имеет размер sizeof (указатель) большой. кардинал на 32-битной, qword на 64-битной.

1 голос
/ 21 февраля 2012

Это еще один возможный вариант решения mghie * :

procedure TForm1.Button1Click(Sender: TObject);
type
  TFFTData = array [0..0] of Double;
  PFFTData = ^TFFTData;
var
  AllocatedBuffer: Pointer;
  AlignedArray: PFFTData;
  i: Integer;
const
  cFFTDataSize=63536;
begin

  GetMem(AllocatedBuffer, cFFTDataSize*SizeOf(Double) + 16);  // e.g 16 Bytes boudaries alignement

  try
    AlignedArray := PFFTData((Integer(AllocatedBuffer) and $FFFFFFF0) + 16);

    // use data...

    for i := 0 to cFFTDataSize-1 do
      AlignedArray[i] := i * Pi;
  finally
    FreeMem(AllocatedBuffer);
  end;
end;

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

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