4

The Embarcadero official said "TFDMemTable is faster than TClientDataSet",But I Make an little speed test and TFDMemtable slower than TClientDataset (Delphi XE7).

{
Test 30k records
            TClientDataSet TFDMemTable
Append         1482ms      2153ms
SaveToFile     125ms       348ms        
SaveToStream   125ms       312ms
LoadFromFile   109ms       686ms
LoadFromStream 234ms       718ms 
}

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Param, FireDAC.Stan.Error,
  FireDAC.DatS, FireDAC.Phys.Intf, FireDAC.DApt.Intf, FireDAC.Comp.DataSet, FireDAC.Comp.Client, Data.DB,
  Datasnap.DBClient, Vcl.StdCtrls, Vcl.Samples.Spin, FireDAC.Stan.StorageBin;

type
  TForm1 = class(TForm)
    btnFDAppend: TButton;
    btnAppend: TButton;
    seCount: TSpinEdit;
    btnFDEmptyDataSet: TButton;
    btnEmptyDataSet: TButton;
    lblClientDataset: TLabel;
    lblFDMemTable: TLabel;
    btnSaveToFile: TButton;
    btnFDSaveToFile: TButton;
    btnLoadFromFile: TButton;
    btnFDLoadFromFile: TButton;
    btnSaveToStream: TButton;
    btnFDSaveToStream: TButton;
    btnFDLoadFromStream: TButton;
    btnLoadFromStream: TButton;
    lblRecordCount: TLabel;
    lblWhy: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure btnFDAppendClick(Sender: TObject);
    procedure btnAppendClick(Sender: TObject);
    procedure btnFDEmptyDataSetClick(Sender: TObject);
    procedure btnLoadFromFileClick(Sender: TObject);
    procedure btnSaveToFileClick(Sender: TObject);
    procedure btnFDSaveToFileClick(Sender: TObject);
    procedure btnFDLoadFromFileClick(Sender: TObject);
    procedure btnSaveToStreamClick(Sender: TObject);
    procedure btnFDSaveToStreamClick(Sender: TObject);
    procedure btnLoadFromStreamClick(Sender: TObject);
    procedure btnFDLoadFromStreamClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure btnEmptyDataSetClick(Sender: TObject);
  private
    FStartTime: DWORD;
    FFDStream, FCDSStream: TMemoryStream;
    FClientDataSet: TClientDataSet;
    FFDMemTable: TFDMemTable;

    function NewFDMemTable: TFDMemTable;
    function NewClientDataSet: TClientDataSet;

    procedure ShowDuration(Sender: TObject);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}


procedure TForm1.btnFDAppendClick(Sender: TObject);
var
  I: Integer;
begin
  FStartTime := GetTickCount;
  FFDMemTable.DisableControls;
  try
    for I := 1 to seCount.Value do
    begin
      FFDMemTable.Append;
      FFDMemTable.FieldByName('ID').AsInteger := I;
      FFDMemTable.FieldByName('Status').AsString := 'Code' + IntToStr(i);
      FFDMemTable.FieldByName('Created').AsDateTime := Date();
      FFDMemTable.FieldByName('Volume').AsFloat := Random(10000);
      FFDMemTable.Post;
    end;

  finally
    FFDMemTable.EnableControls;
  end;
  ShowDuration(Sender);
end;

procedure TForm1.btnLoadFromFileClick(Sender: TObject);
begin
  FStartTime := GetTickCount;
  FClientDataSet.LoadFromFile('CDS.dat');
  ShowDuration(Sender);
end;

procedure TForm1.btnSaveToFileClick(Sender: TObject);
begin
  FStartTime := GetTickCount;
  FClientDataSet.SaveToFile('CDS.dat', dfBinary);
  ShowDuration(Sender);
end;

procedure TForm1.btnFDEmptyDataSetClick(Sender: TObject);
begin
  FStartTime := GetTickCount;
  FFDMemTable.EmptyDataSet;
  ShowDuration(Sender);
end;

procedure TForm1.btnEmptyDataSetClick(Sender: TObject);
begin
  FStartTime := GetTickCount;
  FClientDataSet.EmptyDataSet;
  ShowDuration(Sender);
end;

procedure TForm1.btnFDSaveToFileClick(Sender: TObject);
begin
  FStartTime := GetTickCount;
  FFDMemTable.SaveToFile('FD.dat', sfBinary);
  ShowDuration(Sender);
end;

procedure TForm1.btnSaveToStreamClick(Sender: TObject);
begin
  FCDSStream.Clear;

  FStartTime := GetTickCount;
  FClientDataSet.SaveToStream(FCDSStream, dfBinary);
  ShowDuration(Sender);
end;

procedure TForm1.btnFDLoadFromFileClick(Sender: TObject);
begin
  FStartTime := GetTickCount;
  FFDMemTable.LoadFromFile('FD.dat', sfBinary);
  ShowDuration(Sender);
end;

procedure TForm1.btnFDSaveToStreamClick(Sender: TObject);
begin
  FFDStream.Clear;
  FStartTime := GetTickCount;
  FFDMemTable.SaveToStream(FFDStream, sfBinary);

  ShowDuration(Sender);
end;

procedure TForm1.btnFDLoadFromStreamClick(Sender: TObject);
begin
  FStartTime := GetTickCount;
  FFDStream.Position := 0;
  FFDMemTable.LoadFromStream(FFDStream, sfBinary);
  ShowDuration(Sender);
end;

procedure TForm1.btnLoadFromStreamClick(Sender: TObject);
begin
  FStartTime := GetTickCount;
  FCDSStream.Position := 0;
  FClientDataSet.LoadFromStream(FCDSStream);

  ShowDuration(Sender);
end;

procedure TForm1.btnAppendClick(Sender: TObject);
var
  I: Integer;
begin
  FStartTime := GetTickCount;
  FClientDataSet.DisableControls;
  try
    for I := 1 to seCount.Value do
    begin
      FClientDataSet.Append;
      FClientDataSet.FieldByName('ID').AsInteger := I;
      FClientDataSet.FieldByName('Status').AsString := 'Code' + I.ToString;
      FClientDataSet.FieldByName('Created').AsDateTime := Date();
      FClientDataSet.FieldByName('Volume').AsFloat := Random(10000);
      FClientDataSet.Post;
    end;

  finally
    FClientDataSet.EnableControls;
  end;
  ShowDuration(Sender);

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FFDMemTable := NewFDMemTable;
  // FFDMemTable.FormatOptions.InlineDataSize := 10;
  FClientDataSet := NewClientDataSet;

  FCDSStream := TMemoryStream.Create;
  FFDStream := TMemoryStream.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeAndNil(FFDMemTable);
  FreeAndNil(FClientDataSet);
  FreeAndNil(FCDSStream);
  FreeAndNil(FFDStream);
end;

function TForm1.NewClientDataSet: TClientDataSet;
begin
  Result := TClientDataSet.Create(nil);
  with Result do
  begin
    FieldDefs.Add('ID', ftInteger, 0, False);
    FieldDefs.Add('Status', ftString, 10, False);
    FieldDefs.Add('Created', ftDate, 0, False);
    FieldDefs.Add('Volume', ftFloat, 0, False);
    CreateDataSet;
    LogChanges := False;
  end;
end;

function TForm1.NewFDMemTable: TFDMemTable;
begin
  Result := TFDMemTable.Create(nil);
  with Result do
  begin
    FieldDefs.Add('ID', ftInteger, 0, False);
    FieldDefs.Add('Status', ftString, 10, False);
    FieldDefs.Add('Created', ftDate, 0, False);
    FieldDefs.Add('Volume', ftFloat, 0, False);
    CreateDataSet;
    LogChanges := False;
  end;
end;

procedure TForm1.ShowDuration(Sender: TObject);
var
  s: string;
  idx: Integer;
begin
  s := TButton(Sender).Caption;
  idx := s.IndexOf('-');
  if idx > 0 then
    s := s.Substring(0, idx);

  TButton(Sender).Caption := Format('%s-%dms', [s, (GetTickCount - FStartTime)]);
end;

end.

This is test result screenshot:

enter image description here

This is the test application source.

TLama
  • 75,147
  • 17
  • 214
  • 392
Natural
  • 51
  • 1
  • 4
  • Questions related to code must contain the code *here*, in the question itself. Having all of the relevant content somewhere else means that if that other site is unavailable for some reason, the question has no content or value for future readers. Questions asked here should be self-sufficient and not depend on another site for content. Please [edit] your question and include that content here. – Ken White Nov 13 '14 at 06:39
  • 1
    Try to replace your `DisableControls..EnableControls` block with `BeginBatch..EndBatch`. – TLama Nov 13 '14 at 09:27
  • Also initialize fields before the loop like fldID:= FClientDataSet.FindField('ID') and use fldID in the loop – Ravaut123 Nov 13 '14 at 10:10
  • 4
    Thanks [TLama](http://stackoverflow.com/users/960757/tlama), replace DisableControls..EnableControls block with BeginBatch..EndBatch 64bit TFDMemTable Append faster than TClientDataset. Thanks [Ravaut123](http://stackoverflow.com/users/107018/ravaut123) Your suggestion is away of improve performance. – Natural Nov 13 '14 at 11:28

0 Answers0