Hallo zusammen,
für ein Number-Cruncher-Projekt brauche ich einen vernünftigen Kompromiss aus Speed und komfortable Code-Wartung bzw Code-Verwendung für den Zugriff auf ein dynamisches Array.
Einerseits soll der Zugriff möglichst schnell sein, andererseits sollten diverse OO-Prinzipien eingehalten werden.
Mit folgendem Demo-Prog hab ich mal die Zeiten vergleichen:
Delphi-Quellcode:
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private-Deklarationen }
function getInd( x: integer; aMin: integer ): Integer;
public
{ Public-Deklarationen }
end;
type
TDynValueArray = array of Extended;
TStatValueArray = array [20..120] of Extended;
IDynArray = interface( IInterface )
['{4F626337-44B1-4340-A05C-711A04A0DEB4}']
function getMin(): Integer;
function getMax(): Integer;
function getVal( x: integer ): Extended;
procedure setValue( x: Integer; aValue: Extended );
property val[ index: integer ]: extended read getVal write setValue; default;
property min: Integer read getMin;
property max: Integer read getMax;
end;
TDynArrayImpl = class(TInterfacedObject,IDynArray)
var
Fval: TDynValueArray;
Fmin: Integer;
Fmax: Integer;
function getMin(): Integer;
function getMax(): Integer;
function getVal( x: integer ): Extended;
procedure setValue( x: Integer; aValue: Extended );
constructor Create( aMin: Integer; aMax: Integer );
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
constructor TDynArrayImpl.Create(aMin, aMax: Integer);
var
i: integer;
begin
self.Fmin := aMin;
self.Fmax := aMax;
SetLength(
Self.Fval,
self.Fmax - self.Fmin + 1
);
for i := 0 to Length( self.Fval )-1 do
self.Fval[ i ] := 0;
end;
function TDynArrayImpl.getMax: Integer;
begin
Result := self.Fmax;
end;
function TDynArrayImpl.getMin: Integer;
begin
Result := self.FMin;
end;
function TDynArrayImpl.getVal(x: integer): Extended;
begin
result := Self.Fval[ x - self.FMin ];
end;
procedure TDynArrayImpl.setValue(x: Integer; aValue: Extended);
begin
Self.Fval[ x - self.FMin ] := aValue;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
lF: Extended;
lDynAr: IDynArray;
lPid: Integer;
lMin,lMax,x: Integer;
lDynValAr: TDynValueArray;
lValArStat: TStatValueArray;
const
lcAnzZugriffe = High(Integer);
function toInd(aX: Integer): Integer;
begin
Result := x - lMin;
end;
begin
lMin := 20;
lMax := 120;
x := 80;
// Variante 1: dyn Array hinter einem Interface kapseln
lDynAr := TDynArrayImpl.Create(lMin,lMax);
lPid := gvProfiler.start();
for i := 1 to lcAnzZugriffe do
lF := lDynAr[x];
gvProfiler.stop(lPid,'interface');
SetLength(
lDynValAr,
lMax - lMin + 1
);
// Variante 2: direkter Zugriff auf das dyn Array, Indexverschiebung per function kapseln
lPid := gvProfiler.start();
for i := 1 to lcAnzZugriffe do
lF := lDynValAr[ toInd(x) ];
gvProfiler.stop(lPid,'dynAr direkt, function offset');
// Variante 3: direkter Zugriff auf das dyn Array, direkte Indexverschiebung
lPid := gvProfiler.start();
for i := 1 to lcAnzZugriffe do
lF := lDynValAr[ x - lMin ];
gvProfiler.stop(lPid,'dynAr direkt, offset direkt');
// Zum Vergleich: statisches Array
lPid := gvProfiler.start();
for i := 1 to lcAnzZugriffe do
lF := lValArStat[ x ];
gvProfiler.stop(lPid,'statAr direkt, ohne offset');
end;
function TForm1.getInd(x: integer; aMin: integer): Integer;
begin
Result := x - aMin;
end;
Ergebnis:
Variante1: interface
16832 ms
Variante2: dynAr direkt, function offset
8143 ms
Variante3: dynAr direkt, offset direkt
3822 ms
Vergleich: statAr direkt, ohne offset
3791 ms
Was ich mir nicht erklären kann:
Warum ist der getter-Zugriff des Interfaces (var 1) um Faktor 2 langsamer als die Indexberechung per function (var 2)?
Wie würdet ihr es lösen, wenn zumindest die Indexverschiebung gekapselt sein soll?
Vielen Dank und Grüße