He hecho algunas refactorizaciones realmente serias de mi editor de texto. Ahora hay mucho menos código, y es mucho más fácil extender el componente. Hice un uso bastante fuerte del diseño OO, como las clases abstractas y las interfaces. Sin embargo, he notado algunas pérdidas en lo que respecta al rendimiento. El problema es leer una gran variedad de registros. Es rápido cuando todo sucede dentro del mismo objeto, pero lento cuando se realiza a través de una interfaz. He hecho el programa tinyest para ilustrar los detalles:Delphi Interface Performance Issue
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.
Cuando leí los datos directamente, consigo momentos como 0,14 segundos. Pero cuando paso por la interfaz, demora 1,06 segundos.
¿No hay forma de lograr el mismo rendimiento que antes con este nuevo diseño?
Debo mencionar que he tratado de establecer PArrayOfRecord = ^TArrayOfRecord
y redefinió IMyInterface.arr: PArrayOfRecord
y escribí Arr^
etc en el bucle for
. Esto ayudó mucho; Luego obtuve 0.22 segundos. Pero aún no es lo suficientemente bueno. ¿Y qué lo hace tan lento para empezar?
Sé que esto es solo un programa de prueba creado muy rápido, pero primero configure MyInterface en nil y luego libere MyObject; de lo contrario, se llama a _Release en un objeto liberado. Y usa una prueba ... por fin. Solo para que no establezcas un ejemplo incorrecto para los novatos. –
@The_Fox: listo. –