繁体   English   中英

Delphi数组对齐设置为4,8或16字节边界?

[英]Delphi Array Alignment set to 4, 8, or 16 byte boundaries?

我想使用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字节边界,如果有任何可以清除,那将是伟大的。

谢谢,布鲁斯

请注意,您可以使用可能需要的任何自定义对齐方式创建数据结构。 例如,在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。

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

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

堆块是iirc始终与FastMM对齐16字节的bounderies(旧的D7 memmanager与8对齐)。 我不知道sharemem,因为我不使用它。

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

以十六进制表示的ptruint(@in [0])并查看结尾是0还是8.(*)

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM