0

I was writing a Firemonkey application and ran into a problem: TMemo is painfully slow. It needs 12+ seconds to load a 3 mb file, VCL TMemo only needs 400 ms

Here is my test code:

uses
    System.Threading, System.Diagnostics;

procedure TForm1.FormCreate(Sender: TObject);
begin
  TTask.Run(
    procedure
    begin
      Sleep(100);
      var stopwatch := TStopwatch.Create;

      TThread.Synchronize(nil,
        procedure
        begin
          stopwatch.Start;
          Memo1.Lines.LoadFromFile('../../../DemoData.json');
        end);

      TThread.Synchronize(nil,
        procedure
        begin
          stopwatch.Stop;
          Caption := stopwatch.ElapsedMilliseconds.ToString;
        end)
    end);
end;

The entire test project (including JSON file) can be found here: https://borrisholt.dk/Delphi/MemoDemo.zip

Or your can of course use your own 3mb text file.

Can anything be done or is it just the way it is with FMX?

Jens Borrisholt
  • 6,174
  • 1
  • 33
  • 67
  • 1
    Have you tried loading the file into a TStringList in the background thread and then transferring it to your TMemo? – Alex James Sep 16 '21 at 11:09
  • I have now: Same result. I was expecting that since LoadFromFile uses 8 seconds. – Jens Borrisholt Sep 16 '21 at 11:17
  • 1
    What about with `BeginUpdate`/`EndUpdate` around the loading code? – MartynA Sep 16 '21 at 11:18
  • Tried that aswell, makes no difference. – Jens Borrisholt Sep 16 '21 at 11:20
  • 2
    You would have to write your own. It is possible that you can reuse plenty of existing code. TStringList that backs Lines property is extremely dumb and assigning to it creates copy in rather inefficient manner. What you need is ability to replace Lines reference with another one without creating a copy - in other words separating data container from the control that presents it. That way you would be able to load data in the background thread and swapping the reference in main thread would be just plain pointer assignment and repainting the control. – Dalija Prasnikar Sep 16 '21 at 12:17
  • @JensBorrisholt You do realize that the `TTask` code you have presented is completely useless, don't you? It is delegating all of its work to the main UI thread, defeating the purpose of using `TTask` at all. The only thing actually running in the worker thread is the `Sleep()` and `TStopwatch.Create`. You may as well have just used `TThread.ForceQueue()` instead. – Remy Lebeau Sep 16 '21 at 17:41
  • No it's not useless. It assures that the GUI will show before it is locked – Jens Borrisholt Sep 16 '21 at 17:48
  • Here in both examples I added `Caption := Caption + ' ' + IntToStr(Memo1.Lines.Count);` under where it is currently set. The VCL version loads it as multiple lines while the FMX version does not. Turning word wrap on for the FMX memo version still loads it in one line but speeds up display. – Brian Sep 16 '21 at 21:20
  • You could load the "number of lines", and only read the actual lines you're going to display. It'd make scrolling slower, but should improve the initial load time by a fair bit. And you could always load more data from the current view position as a background task. – T.S Sep 17 '21 at 11:31

1 Answers1

2
Memo1.ControlType:=Platform;

If you do, you can get the performance in vcl.