Ich habe ein paar wirklich ernsthafte Refactoring von meinem Texteditor gemacht. Jetzt gibt es viel weniger Code und es ist viel einfacher, die Komponente zu erweitern. Ich habe den OO-Entwurf, wie zB abstrakte Klassen und Interfaces, ziemlich stark genutzt. Ich habe jedoch ein paar Verluste in Bezug auf die Leistung bemerkt. Das Problem besteht darin, eine sehr große Reihe von Datensätzen zu lesen. Es ist schnell, wenn alles innerhalb desselben Objekts geschieht, aber langsam, wenn es über eine Schnittstelle erfolgt. Ich habe das tinyest Programm zu veranschaulichen die Details gemacht:Delphi Interface Performance Ausgabe
unit Unit3;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
const
N = 10000000;
type
TRecord = record
Val1, Val2, Val3, Val4: integer;
end;
TArrayOfRecord = array of TRecord;
IMyInterface = interface
['{C0070757-2376-4A5B-AA4D-CA7EB058501A}']
function GetArray: TArrayOfRecord;
property Arr: TArrayOfRecord read GetArray;
end;
TMyObject = class(TComponent, IMyInterface)
protected
FArr: TArrayOfRecord;
public
procedure InitArr;
function GetArray: TArrayOfRecord;
end;
TForm3 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form3: TForm3;
MyObject: TMyObject;
implementation
{$R *.dfm}
procedure TForm3.FormCreate(Sender: TObject);
var
i: Integer;
v1, v2, f: Int64;
MyInterface: IMyInterface;
begin
MyObject := TMyObject.Create(Self);
try
MyObject.InitArr;
if not MyObject.GetInterface(IMyInterface, MyInterface) then
raise Exception.Create('Note to self: Typo in the code');
QueryPerformanceCounter(v1);
// APPROACH 1: NO INTERFACE (FAST!)
// for i := 0 to high(MyObject.FArr) do
// if (MyObject.FArr[i].Val1 < MyObject.FArr[i].Val2) or
// (MyObject.FArr[i].Val3 < MyObject.FArr[i].Val4) then
// Tag := MyObject.FArr[i].Val1 + MyObject.FArr[i].Val2 - MyObject.FArr[i].Val3
// + MyObject.FArr[i].Val4;
// END OF APPROACH 1
// APPROACH 2: WITH INTERFACE (SLOW!)
for i := 0 to high(MyInterface.Arr) do
if (MyInterface.Arr[i].Val1 < MyInterface.Arr[i].Val2) or
(MyInterface.Arr[i].Val3 < MyInterface.Arr[i].Val4) then
Tag := MyInterface.Arr[i].Val1 + MyInterface.Arr[i].Val2 - MyInterface.Arr[i].Val3
+ MyInterface.Arr[i].Val4;
// END OF APPROACH 2
QueryPerformanceCounter(v2);
QueryPerformanceFrequency(f);
ShowMessage(FloatToStr((v2-v1)/f));
finally
MyInterface := nil;
MyObject.Free;
end;
end;
{ TMyObject }
function TMyObject.GetArray: TArrayOfRecord;
begin
result := FArr;
end;
procedure TMyObject.InitArr;
var
i: Integer;
begin
SetLength(FArr, N);
for i := 0 to N - 1 do
with FArr[i] do
begin
Val1 := Random(high(integer));
Val2 := Random(high(integer));
Val3 := Random(high(integer));
Val4 := Random(high(integer));
end;
end;
end.
Wenn ich die Daten direkt lesen, ich mal wie 0,14 Sekunden erhalten. Aber wenn ich durch die Schnittstelle gehe, dauert es 1,06 Sekunden.
Gibt es keine Möglichkeit, mit diesem neuen Design die gleiche Leistung wie zuvor zu erzielen?
Ich sollte erwähnen, dass ich versucht PArrayOfRecord = ^TArrayOfRecord
zu setzen und neu definiert IMyInterface.arr: PArrayOfRecord
und schrieb Arr^
usw. in der for
Schleife. Das hat sehr geholfen; Ich habe dann 0,22 Sekunden bekommen. Aber es ist immer noch nicht gut genug. Und warum ist es so langsam?
Ich weiß, das ist nur ein Testprogramm wirklich schnell zusammengeworfen, aber bitte zuerst MyInterface auf Null und dann frei MyObject, sonst _Release wird auf einem freigegebenen Objekt aufgerufen. Und benutze einen Versuch..finally. Nur damit Sie den Neulingen kein falsches Beispiel geben. –
@The_Fox: Fertig. –