apidelphifiremonkeytgridfdmemtable

Slow performance of TGrid everytime it is created at runtime through dynamic (increasing data) TFDMemTable definition provided REST API data


I am developing Firemonkey App for iOS and Android. I noticed that the app gets slower performance in iOS and Android debugging everytime the TGrid is created at runtime using the TFDMemTable REST API data and structure.

I already applied the FreeAndNil(TGrid1); to cleanup TGrid before it creates over and over again.

One notable event, everytime the TGrid is created the rows are increasing with fixed 7 columns the performance gets slower. Normally, this happens when I reached to 10 rows or records.

My one big and real quick question:

Where do you think the overheads are coming from that caused the performance to slow?

  1. TGrid — which I already applied FreeAndNil(TGrid1); before its creation.

  2. TFMemTable — I haven't checked this and I do not know how

  3. TButton — the one that triggers the creation and loading of data to TGrid. Most of the codes reside here

Let's assume that all other components are working fine prior to this case. I could share to you some code if you want to but guide me which one you want to see.

UPDATE 1 : MINIMUM REPRODUCIBLE EXAMPLE

FMX File

object Form9: TForm9
  Left = 0
  Top = 0
  Caption = 'MRE TeeGrid Runtime'#13#10
  ClientHeight = 480
  ClientWidth = 294
  FormFactor.Width = 320
  FormFactor.Height = 480
  FormFactor.Devices = [Desktop]
  DesignerMasterStyle = 0
  object btn1: TButton
    Align = Bottom
    Position.Y = 440.000000000000000000
    Size.Width = 294.000000000000000000
    Size.Height = 40.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 9
    Text = 'CREATE TEEGRID'
    OnClick = btn1Click
  end
  object aniSearchProcess: TAniIndicator
    Position.X = 128.000000000000000000
    Position.Y = 216.000000000000000000
  end
  object lyt1: TLayout
    Align = Client
    Size.Width = 294.000000000000000000
    Size.Height = 440.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 11
  end
  object cur1: TFDGUIxWaitCursor
    Provider = 'FMX'
    Left = 32
    Top = 32
  end
  object dvr1: TFDPhysSQLiteDriverLink
    Left = 88
    Top = 32
  end
  object con1: TFDConnection
    Params.Strings = (
      'DriverID=SQLite')
    Connected = True
    LoginPrompt = False
    Left = 144
    Top = 32
  end
  object loc1: TFDLocalSQL
    Connection = con1
    Active = True
    Left = 200
    Top = 32
  end
  object rsc1: TRESTClient
    Accept = 'application/json, text/plain; q=0.9, text/html;q=0.8,'
    AcceptCharset = 'utf-8, *;q=0.8'
    BaseURL = 
      'https://me6hwinr2k.execute-api.ap-southeast-1.amazonaws.com/v0/d' +
      'bqueries?item-var=9&qty=25'
    Params = <>
    Left = 32
    Top = 112
  end
  object rsq1: TRESTRequest
    Client = rsc1
    Params = <>
    Response = rsp1
    SynchronizedEvents = False
    Left = 32
    Top = 176
  end
  object rsp1: TRESTResponse
    ContentType = 'application/json'
    Left = 32
    Top = 240
  end
  object rsd1: TRESTResponseDataSetAdapter
    Active = True
    Dataset = mtb1
    FieldDefs = <>
    Response = rsp1
    Left = 32
    Top = 304
  end
  object mtb1: TFDMemTable
    Active = True
    FieldDefs = <
      item
        Name = 'Category'
        DataType = ftWideString
        Size = 255
      end
      item
        Name = 'ID'
        DataType = ftWideString
        Size = 255
      end
      item
        Name = 'Item'
        DataType = ftWideString
        Size = 255
      end
      item
        Name = 'Qty'
        DataType = ftWideString
        Size = 255
      end
      item
        Name = 'Container'
        DataType = ftWideString
        Size = 255
      end
      item
        Name = 'Size'
        DataType = ftWideString
        Size = 255
      end
      item
        Name = 'Ex temporibus dolore consequatur.'
        DataType = ftWideString
        Size = 255
      end
      item
        Name = 'Et cum aut est nostrum...'
        DataType = ftWideString
        Size = 255
      end
      item
        Name = 'Sequi quibusdam eum.'
        DataType = ftWideString
        Size = 255
      end>
    IndexDefs = <>
    FetchOptions.AssignedValues = [evMode]
    FetchOptions.Mode = fmAll
    ResourceOptions.AssignedValues = [rvSilentMode]
    ResourceOptions.SilentMode = True
    UpdateOptions.AssignedValues = [uvCheckRequired, uvAutoCommitUpdates]
    UpdateOptions.CheckRequired = False
    UpdateOptions.AutoCommitUpdates = True
    StoreDefs = True
    Left = 32
    Top = 368
  end
end

FMX Procedures

unit Main;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FireDAC.UI.Intf, FireDAC.FMXUI.Wait, FireDAC.Stan.ExprFuncs,
  FireDAC.Phys.SQLiteDef, FireDAC.Stan.Intf, FireDAC.Stan.Option,
  FireDAC.Stan.Error, FireDAC.Phys.Intf, FireDAC.Stan.Def, FireDAC.Stan.Pool,
  FireDAC.Stan.Async, FireDAC.Phys, FireDAC.Phys.SQLite, Data.DB,
  FireDAC.Stan.Param, FireDAC.DatS, FireDAC.DApt.Intf, REST.Types,
  FMX.Controls.Presentation, FMX.StdCtrls, FireDAC.Comp.DataSet,
  FireDAC.Comp.Client, REST.Response.Adapter, REST.Client, Data.Bind.Components,
  Data.Bind.ObjectScope, FireDAC.Phys.SQLiteVDataSet, FireDAC.Comp.UI,
  FMXTee.Control, FMXTee.Grid, FMX.Layouts;

type
  TForm9 = class(TForm)
    cur1: TFDGUIxWaitCursor;
    dvr1: TFDPhysSQLiteDriverLink;
    con1: TFDConnection;
    loc1: TFDLocalSQL;
    rsc1: TRESTClient;
    rsq1: TRESTRequest;
    rsp1: TRESTResponse;
    rsd1: TRESTResponseDataSetAdapter;
    mtb1: TFDMemTable;
    btn1: TButton;
    aniSearchProcess: TAniIndicator;
    lyt1: TLayout;
    procedure btn1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form9: TForm9;
  tgd1: TTeeGrid;

implementation

{$R *.fmx}

procedure TForm9.btn1Click(Sender: TObject);
var
  i, CanvassItemId, e : Integer;
begin
  aniSearchProcess.Visible := True;
  aniSearchProcess.Enabled := True;
  {$IFDEF MSWINDOWS}
    Application.ProcessMessages;
  {$ENDIF}
  {$IF DEFINED(iOS) or DEFINED(ANDROID)}
    Application.HandleMessage;
  {$ENDIF}

  FreeAndNil(tgd1); //free old grid

  //create new grid
  tgd1 := TTeeGrid.Create(lyt1);
  With tgd1 do
  begin
    Parent := lyt1;
    Align := TAlignLayout.Client;
    Margins.Top := 5;
    Margins.Left := 5;
    Margins.Right := 5;
    Margins.Bottom := 5;
    ScrollBars.Visible := True;
    Header.Format.Font.Size := 11;
    Cells.Format.Font.Size := 11;
    TabOrder := 0;
    ScrollBars.Visible := False;
  end;

  con1.StartTransaction;
  try
    //define the API here for duplicate/update, initial click and subsequent clicks
    rsc1.BaseURL := 'https://0rgvn0s0gk.execute-api.ap-southeast-1.amazonaws.com/v0/dbqueries?item-var=1&qty=10';
    rsq1.Execute;
    rsd1.Active := True;
    mtb1.Active;
    tgd1.DataSource := mtb1;
    tgd1.Enabled := True;

    // adjust the column properties dynamically
    with tgd1 do
    begin
      for i := 0 to Columns.Count -1 do
      begin
        if i = 0 then
        begin
          Columns[i].Visible := False; // category column
        end
        else if (i = 1) then
        begin
          Columns[i].Visible := False; // id column
        end
        else if (i = 2) then
        begin
          Columns[i].Width.Value := 120; // item column
        end
        else if (i = 3) then
        begin
          Columns[i].Width.Value := 30; // qty column
        end
        else if (i = 4) then
        begin
          Columns[i].Width.Value := 50; // container column
        end
        else if (i = 5) then
        begin
          Columns[i].Width.Value := 50; // size column
        end
        else
        begin
          Columns[i].Width.Value := 50; // subsequent random columns
        end;
      end;
    end;
  finally
    con1.Commit;
  end;

  aniSearchProcess.Visible := False;
  aniSearchProcess.Enabled := False;
  {$IFDEF MSWINDOWS}
    Application.ProcessMessages;
  {$ENDIF}
  {$IF DEFINED(iOS) or DEFINED(ANDROID)}
    Application.HandleMessage;
  {$ENDIF}

end;

end.

Solution

  • The creation of TTeeGrid at runtime accumulates overheads that slows down the performance at some point.

    To solve it, I have removed the creation and freeing of TTeeGrid at runtime, instead, I placed TTeeGrid visual component at design time and have it refresh its connection by property enabled to true or false every time it is triggered by the new set of data and structure provided by API.