Hallo liebe Leute!
Ich suche derzeit, ohne besonderem Hintergrund, einen möglichst generischen Weg um
eine Operation möglichst schnell auf einem gesamten Array auszuführen.
Mit Anonymen Methoden lässt sich das ja relativ einfach realisieren:
Delphi-Quellcode:
unit Unit1;
interface
uses
Winapi.Windows,
Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs;
type
TProc = reference
to Function(aValue: Integer): Integer;
const
count = 10000000;
// 10 Millionen
type
TForm1 =
class(TForm)
procedure FormClick(Sender: TObject);
private
arr:
array [0 .. count - 1]
of Integer;
Procedure ForEachMul(
const aFactor: Integer);
Procedure ForEach(
const aProc: TProc);
procedure Output(
const TimeNormal, TimeAnon: double);
Procedure Test;
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.ForEach(
const aProc: TProc);
var
I: Integer;
begin
for I := 0
to count - 1
do
begin
Self.arr[I] := aProc(arr[I]);
end;
end;
procedure TForm1.ForEachMul(
const aFactor: Integer);
var
I: Integer;
begin
for I := 0
to count - 1
do
begin
Self.arr[I] := Self.arr[I] * aFactor;
end;
end;
procedure TForm1.FormClick(Sender: TObject);
begin
Test;
end;
procedure TForm1.Output(
const TimeNormal, TimeAnon: double);
begin
Self.Canvas.TextOut(10, 10, '
Zeit normal: ' + TimeNormal.ToString());
Self.Canvas.TextOut(10, 15 + Self.Canvas.TextHeight('
Text'),
'
Zeit anon: ' + TimeAnon.ToString());
end;
procedure TForm1.Test;
var
t1, t2, t3, f: Int64;
begin
QueryPerformanceFrequency(f);
QueryPerformanceCounter(t1);
Self.ForEachMul(2);
QueryPerformanceCounter(t2);
Self.ForEach(
Function(aValue: Integer): Integer
begin
result := aValue * 2;
end);
QueryPerformanceCounter(t3);
Self.Output((t2 - t1) / f, (t3 - t2) / f);
end;
end.
Leider scheint es ein Grundgesetz zu sein, dass Flexibilität und Geschwindigkeit umgekehrt
proportional zusammenhängen
Mit der Anonymen Methode ist halt das Problem, dass bei jedem Aufruf eine Referenz aufgelöst
werden muss und auch sonst noch einiges an Verwaltungsaufwand betrieben werden wird.
Vermutlich daher benötigt diese Version auch fast die doppelte Zeit der normalen Methode
(auf meinem Rechner).
Gibt es schnellere Methoden, ohne Flexibilität aufgeben zu müssen?
Ich bin über jede Idee, der ich nachgehen kann, sehr dankbar!
nachgepatcht:
Ich weiß natürlich, dass ich das auch mit Pointern auf Prozeduren und ähnlichen Konstrukten erreichen
kann. Das würde wahrscheinlich sogar ein kleines Etwas an Geschwindigkeit bringen.
Aber eines der Hauptprobleme bleibt: Es ist vom Prozessor (und ein paar anderen Dingen) abhängig,
ob ich dann bei der Verarbeitung eines jeden Arrays ein Cache-Miss bekomme, weil der Programmcounter
dauernd herumspringt. Selbst wenn der L1-Cache als Set-Associative-Cache ausgeführt ist, sollte das
zu Geschwindigkeitseinbußen führen.
Mein Traum wäre insofern natürlich, wenn ich irgendwie von außen in die ForEach-Methode einen Code
einpflanzen könnte (statt der Multiplikation im Code oben, der dann innerhalb dieser Methode ausgeführt
wird. Der Code kann dort zur Laufzeit auch gerne statisch bleiben.
Im Prinzip würde es mir darum gehen, verschiedene Operationen, die ich auf Arrays anwenden möchte,
an einer anderen Stelle (
Unit, Klasse, etc...) semantisch zusammen zu fassen (z.B. Physik.Kinematik...).