2

我想使用 Delphi 2009 中的 FFTW C 库并根据此文档;

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 字节的边界,如果有的话可以明确这一点。

谢谢,布鲁斯

4

4 回答 4

6

请注意,您可以使用您可能需要的任何自定义对齐方式创建数据结构。例如,在 128 字节边界上对齐 FFT 数据:

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;

编辑:

关于分配两次内存的评论:堆栈变量 FFTData 的类型是 PFFTData,而不是 TFFTData,所以它是一个指针。这不是那么明显,因为语法增强允许省略 ^ 以取消引用指针。内存是使用 GetMem() 分配的,并且使用正确的类型而不是使用类型转换的无类型内存块。我可能应该称它为 FFTDataPtr。

于 2009-05-11T07:16:08.320 回答
4

Delphi 无法控制它分配的任何内存的对齐方式。正如 Mghie 演示的那样,您要么依赖于当前安装的内存管理器的记录行为,要么分配具有一些松弛空间的内存,然后自行对齐。

如果您担心 Delphi 的内存管理器没有为动态数组提供所需的对齐方式,那么您可以继续使用 DLL 提供的内存函数。您引用的注释提到_fftwf_mallocand _fftwf_free,但随后它给出了某种警告,即从_fftwf_malloc“可能无法直接从 Delphi 访问”分配的内存。不过,这不是作者的意思,因为这不是 Windows 中内存的工作方式。作者可能的意思是说_fftwf_mallocDelphi 无法释放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异常。相反,您将获得损坏的内存、访问冲突或神秘的程序崩溃。

于 2009-05-11T08:11:02.550 回答
3

堆块总是通过 FastMM 对齐到 16 字节边界(旧的 D7 内存管理器对齐到 8)。我不知道sharemem,因为我不使用它。

动态数组是基于堆的结构。OTOH dyn 数组可能会变得未对齐(从 16 到 8),因为有一个长度和引用计数前缀。最简单的就是打印

十六进制的 ptruint(@in[0]) 看看结尾是 0 还是 8。 (*)

请注意,FPC 中有 fftw 标头。(packages/fftw),afaik 它最近甚至被修复为 64 位。

我不知道 Delphi 中的堆栈对齐指令。也许它们会自动“自然”对齐。

(*) ptruint 是 FPC 代表 sizeof(pointer) 大的无符号整数类型。32 位上的基数,64 位上的 qword。

于 2009-05-11T07:00:13.587 回答
1

这是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;

我重构了这段代码,使其更有意义,并使用了类似的手动对齐修复技术。

于 2012-02-21T14:02:40.047 回答