Aligned Arrays Implementation
This is an implementation of page size aligned arrays. It is interchangeable with the standard dynamic array type and can implicitly convert between the two. TheTAlignedArray<T>
type exposes aproximately the same functionality as dynamic arrays, but with page sized and aligned pointers. It uses the new management operators to allocate and deallocate memory.Please note, that when you assign one aligned array to another aligned array, new page aligned memory is allocated and memory is copied. If you want to prevent this from happening you should either pass the arrays as constref or by reference. You can use the type
PAlignedArrayType
as a way of passing references safely without falling back to using var
arguments.Here is the basic usage of
TAlignedArray<T>
:
procedure Test; var A, B: TAlignedArray<TPoint>; C: array of TPoint; P: TPoint; begin A.Push(Point(12, 15)); A.Push(Point(8, 4)); B := A; WriteLn('Memory address of first A item ', IntPtr(A[0])); WriteLn('First item of A is page aligned: ', IntPtr(A[0]) mod PageSize = 0); WriteLn('Memory address of first B item ', IntPtr(B[0])); WriteLn('First item of B is page aligned: ', IntPtr(B[0]) mod PageSize = 0); WriteLn('Points in A:'); for P in A do WriteLn('X: ', P.X, ', Y: ', P.Y); WriteLn('Points in B:'); for P in B do WriteLn('X: ', P.X, ', Y: ', P.Y); C := A; WriteLn('Copied to standard dynamic array C'); WriteLn('Points in C:'); for P in C do WriteLn('X: ', P.X, ', Y: ', P.Y); end;Which results in this:
Memory address of first A item 14606336 First item of A is page aligned: TRUE Memory address of first B item 14614528 First item of B is page aligned: TRUE Points in A: X: 12, Y: 15 X: 8, Y: 4 Points in B: X: 12, Y: 15 X: 8, Y: 4 Copied to standard dynamic array C Points in C: X: 12, Y: 15 X: 8, Y: 4If you want to increase the speed when working with aligned arrays, you can set the length directly on an aligned array, request the first item, and safely access subsequent items using the
Inc
procedure.And here is the implementation of
TAlignedArray<T>
:
unit AlignArrays; {$mode delphi} interface type TMemoryEnumerator<T> = class(TInterfacedObject, IEnumerator<T>) private type PItem = ^T; private FMemory: Pointer; FPosition: Integer; FCount: Integer; public constructor Create(Memory: Pointer; Count: Integer); function GetCurrent: T; function MoveNext: Boolean; procedure Reset; property Current: T read GetCurrent; end; TAlignedArray<T> = record public type TArray = array of T; TAlignedArrayType = TAlignedArray<T>; PAlignedArrayType = ^TAlignedArrayType; TReference = ^T; TValue = T; IEnumeratorType = IEnumerator<T>; function GetEnumerator: IEnumeratorType; private FPage: Pointer; FCount: Integer; FLength: Integer; procedure SetLength(Value: Integer); function GetReference(Index: Integer): TReference; function GetItem(Index: Integer): TValue; procedure SetItem(Index: Integer; const Value: TValue); public class operator Initialize(var A: TAlignedArrayType); class operator Finalize(var A: TAlignedArrayType); class operator Copy(constref Source: TAlignedArrayType; var Dest: TAlignedArrayType); class operator Implicit(const Value: TAlignedArrayType): TArray; class operator Implicit(const Value: TArray): TAlignedArrayType; class operator Implicit(const Value: array of T): TAlignedArrayType; procedure Clear; procedure Push(const Item: TValue); function Pop: TValue; property Length: Integer read FLength write SetLength; property Reference[Index: Integer]: TReference read GetReference; default; property Item[Index: Integer]: TValue read GetItem write SetItem; end; const PageSize = 1024 * 4; function PagesAlloc(Count: Integer): Pointer; procedure PagesFree(var Page: Pointer); implementation {$ifdef unix} const libc = 'libc'; function posix_memalign(out ptr: Pointer; alignment: IntPtr; size: IntPtr): Integer; cdecl; external libc; procedure free(ptr: Pointer); cdecl; external libc; {$endif} {$ifdef windows} const msvcrt = 'msvcrt.dll'; function _aligned_malloc(size: IntPtr; alignment: IntPtr): IntPtr; cdecl; external msvcrt; function _aligned_free(memblock: IntPtr): IntPtr; cdecl; external msvcrt; {$endif} function PagesAlloc(Count: Integer): Pointer; {$ifdef unix} var I: Integer; begin if Count > 0 then begin I := posix_memalign(Result, PageSize, Count * PageSize); if I <> 0 then Result := nil; end else Result := nil; end; {$endif} {$ifdef windows} begin if Count > 0 then Result := _aligned_malloc(Count * PageSize, PageSize) else Result := nil; end; {$endif} procedure PagesFree(var Page: Pointer); var P: Pointer; begin P := Page; Page := nil; if P <> nil then {$ifdef unix} free(P); {$endif} {$ifdef windows} _aligned_free(P); {$endif} end; { TMemoryEnumerator<T> } constructor TMemoryEnumerator<T>.Create(Memory: Pointer; Count: Integer); begin inherited Create; FMemory := Memory; FPosition := -1; FCount := Count; end; function TMemoryEnumerator<T>.GetCurrent: T; var Item: PItem; begin Item := FMemory; Inc(Item, FPosition); Result := Item^; end; function TMemoryEnumerator<T>.MoveNext: Boolean; begin Inc(FPosition); Result := FPosition < FCount; end; procedure TMemoryEnumerator<T>.Reset; begin FPosition := -1; end; { TAlignedArray<T> } function TAlignedArray<T>.GetEnumerator: IEnumeratorType; begin Result := TMemoryEnumerator<T>.Create(FPage, FLength); end; class operator TAlignedArray<T>.Initialize(var A: TAlignedArrayType); begin A.FPage := nil; A.FCount := 0; A.FLength := 0; end; class operator TAlignedArray<T>.Finalize(var A: TAlignedArrayType); begin A.Clear; end; class operator TAlignedArray<T>.Copy(constref Source: TAlignedArrayType; var Dest: TAlignedArrayType); var S, D: TReference; I: Integer; begin if @Source = @Dest then Exit; Dest.Length := Source.Length; S := Source.FPage; D := Dest.FPage; for I := 0 to Source.Length - 1 do begin D^ := S^; Inc(S); Inc(D); end; end; class operator TAlignedArray<T>.Implicit(const Value: TAlignedArrayType): TArray; var I: Integer; begin System.SetLength(Result, Value.Length); for I := 0 to Value.Length - 1 do Result[I] := Value.Item[I]; end; class operator TAlignedArray<T>.Implicit(const Value: TArray): TAlignedArrayType; var R: TReference; I: Integer; begin Result.Length := System.Length(Value); R := Result.FPage; for I := 0 to Result.Length - 1 do begin R^ := Value[I]; Inc(R); end; end; class operator TAlignedArray<T>.Implicit(const Value: array of T): TAlignedArrayType; var R: TReference; I: Integer; begin Result.Length := System.Length(Value); R := Result.FPage; for I := 0 to Result.Length - 1 do begin R^ := Value[I]; Inc(R); end; end; procedure TAlignedArray<T>.Clear; begin Length := 0; end; procedure TAlignedArray<T>.Push(const Item: TValue); var I: Integer; begin I := FLength; SetLength(FLength + 1); GetReference(I)^ := Item; end; function TAlignedArray<T>.Pop: TValue; var I: Integer; begin I := FLength - 1; if I > -1 then begin Result := GetReference(I)^; GetReference(I)^ := Default(TValue); SetLength(I); end; end; procedure TAlignedArray<T>.SetLength(Value: Integer); var Buffer: TReference; A, B: TReference; I: Integer; begin if Value < 1 then begin if FLength = 0 then Exit; A := FPage; for I := 0 to FLength - 1 do begin A^ := Default(TValue); Inc(A); end; FCount := 0; FLength := 0; PagesFree(FPage); end else if Value < FLength then begin A := FPage; Inc(A, FLength - 1); for I := FLength -1 downto Value do begin A^ := Default(TValue); Dec(A); end; FLength := Value; end else if Value > FLength then begin I := Value div SizeOf(TValue) + 1; if I > FCount then begin Buffer := PagesAlloc(I); FillChar(Buffer^, PageSize * I, 0); B := Buffer; for I := 0 to Value - 1 do begin B^ := Default(TValue); Inc(B); end; A := FPage; B := Buffer; for I := 0 to FLength - 1 do begin B^ := A^; A^ := Default(TValue); Inc(A); Inc(B); end; PagesFree(FPage); FPage := Buffer; FCount := Value div SizeOf(TValue) + 1; FLength := Value; end else begin A := FPage; Inc(A, FLength); for I := FLength to Value - 1 do begin A^ := Default(TValue); Inc(A); end; FLength := Value; end; end; end; function TAlignedArray<T>.GetReference(Index: Integer): TReference; begin if Index < 0 then Exit(nil); if Index > FLength - 1 then Exit(nil); Result := TReference(FPage); if Index > 0 then Inc(Result, Index); end; function TAlignedArray<T>.GetItem(Index: Integer): TValue; begin Result := GetReference(Index)^; end; procedure TAlignedArray<T>.SetItem(Index: Integer; const Value: TValue); begin GetReference(Index)^ := Value; end; end.