views:

846

answers:

3

I would like to use the FFTW C library from Delphi 2009 and according to this documentation;

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

to increase the performance inside the FFTW library (such that it can use SIMD extensions) arrays passed in of either Single (float) or Double (double) need to be aligned either at 4 or 8 byte boundaries. I found documentation talking about alignment of record structures, but nothing specific about arrays. Is there a way to do this in Delphi 2009.

So the code (copied from the above documentation) would look like this;

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);

Also in the above documentation they talk about 8 and 16 byte boundaries but it looks to me it should be 4 and 8 byte boundaries, if any could clear that up to that would be great.

Thanks, Bruce

+2  A: 

Heap blocks are iirc always aligned to 16-byte bounderies by FastMM (the old D7 memmanager aligned to 8). I don't know about sharemem, since I don't use it.

And dynamic arrays are heap based structures. OTOH dyn arrays could maybe become unaligned (from 16 to 8) because there is a length and ref count prefixed. Easiest is to simply print

ptruint(@in[0]) in hex and see if the end is 0 or 8. (*)

Note that there are fftw headers in FPC. ( packages/fftw), afaik it was recently fixed for 64-bit even.

I'm not aware of Stack alignment directives in Delphi. Maybe they are automatically "naturally" aligned though.

(*) ptruint is FPC speak for an unsigned integer type that is sizeof(pointer) large. cardinal on 32-bit, qword on 64-bit.

Marco van de Voort
+4  A: 

Note that you can create data structures with any custom alignment you might need. For example to align your FFT data on 128 byte boundaries:

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:

Regarding the comment about twice the memory being allocated: The stack variable FFTData is of type PFFTData, not of TFFTData, so it's a pointer. It's not that obvious because of the syntax enhancement allowing to omit the ^ for dereferencing the pointer. The memory is allocated with GetMem(), and to work with the proper type instead of the untyped memory block the typecast is employed. I should probably have called it FFTDataPtr.

mghie
Nice pointer trick.
skamradt
Am I reading this right in that it is allocating the array on the stack TFFTData = array[0..63535] of double; and then again on the heap in GetMem(Buffer, SizeOf(TFFTData) + Alignment); so you end up allocating twice the memory and you probably don't need to allocate the array as it isn't used?
Bruce
I have added some explanation, hope it's sufficient.
mghie
Also, I have seen that your question has code using singles while my code uses doubles, please adjust accordingly.
mghie
+3  A: 

Delphi provides no way to control the alignment of any memory it allocates. You're left to either rely on the documented behavior for the memory manager currently installed, or allocate memory with some slack space and then align it yourself, as Mghie demonstrates.

If you're concerned that Delphi's memory manager is not providing the desired alignment for dynamic arrays, then you can go ahead and use the memory functions provided by the DLL. The note you cite mentions _fftwf_malloc and _fftwf_free, but then it gives some kind of warning that memory allocated from _fftwf_malloc "may not be accessed directly from Delphi." That can't be what the authors meant to say, though, because that's not how memory works in Windows. The authors probably meant to say that memory allocated by _fftwf_malloc cannot be freed by Delphi's FreeMem, and memory allocated by Delphi's GetMem cannot be freed by _fftwf_free. That's nothing special, though; you always need to keep your memory-management functions paired together.

If you use _fftwf_malloc to get your array, then you can access it through an ordinary pointer type. For example:

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

As of Delphi 2009, you can even use array syntax on those pointers:

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

In order to enable that, use the {$POINTERMATH ON} compiler directive; it's not enabled by default except for the character-pointer types.

The disadvantage to manually allocating arrays like this is that you lose range checking. If you index beyond the end of an array, you won't get an easy-to-recognize ERangeError exception anymore. You'll get corrupted memory, access violations, or mysteriously crashing programs instead.

Rob Kennedy
Nice answer, +1. Using the DLL exported memory management functions in a wrapper class for the data arrays would probably work well.
mghie