Проблема с производительностью интерфейса Delphi

Я провел действительно серьезный рефакторинг своего текстового редактора. Теперь кода намного меньше, и расширить компонент намного проще. Я довольно активно использовал объектно-ориентированный дизайн, такой как абстрактные классы и интерфейсы. Однако я заметил несколько потерь в производительности. Проблема заключается в чтении очень большого массива записей. Это быстро, когда все происходит внутри одного объекта, и медленно, когда это делается через интерфейс. Я сделал самую маленькую программу, чтобы проиллюстрировать детали:

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.

Когда я читаю данные напрямую, я получаю время 0,14 секунды. Но когда я просматриваю интерфейс, это занимает 1,06 секунды.

Нет ли способа достичь той же производительности, что и раньше, с этим новым дизайном?

Я должен упомянуть, что я пытался установить PArrayOfRecord = ^ TArrayOfRecord и переопределил IMyInterface.arr: PArrayOfRecord и написал Arr ^ и т. Д. В цикле for . Это очень помогло; Тогда у меня получилось 0,22 секунды. Но этого все еще недостаточно. И что делает его таким медленным?

24
задан Andreas Rejbrand 22 October 2010 в 15:38
поделиться