Speeding Up Gui Work

by Erick Engelke
September 8, 2024

Often powerful Web apps must do some GUI preparation, creating a lot of data for display on screen. But that can be slow.

Consider this basic code to write a few thousand lines to a MultiLineEdit text box:

procedure TForm1.LongOperationSlow;
var
  i : integer;
begin

  MultiLineEdit1.Lines.Clear;
  for i := 0 to 2000 do
     MultilineEdit1.Lines.Add('line ' + IntToStr( i ));
end;

It takes about 4 seconds on a modern browser. This raises some questions:

How do you know that it takes, that long, and how can we speed it up.

The first step is to write a quick little profiling tool which measures the speed in milliseconds. The DateTime in EWB is a millisecond clock.

So here is a routine to time a procedure and return the number of milliseconds it takes to execute.

type
  proc = procedure of object;

function TimeThis( p : proc ) : double;
var
  timer : DateTime;
  seconds : double;
begin
   timer := now;
   p;  // execute the procedure
   seconds := (Integer(now) - Integer(timer)) / 1000;
   result := seconds;
end;

With that function we’ll can time different versions of the procedure that accomplish the same thing.

And we’ll make a TButton which, when clicked, will call each of the four procedures we’ll try, and see how each stacks up.

Here’s the first fast version

procedure TForm1.LongOperationFast;
var
  i : integer;
begin
  // turn off the visibility of the TMultiLineEdit
  MultiLineEdit1.Visible := False;

  MultiLineEdit1.Lines.Clear;
  for i := 0 to 2000 do
     MultilineEdit1.Lines.Add('line ' + IntToStr( i ));

  MultiLineEdit1.Visible := True;
end;

That reduces the time from 4.261 seconds to 0.129. Roughly a 40 times improvement.

The downside is that you would have to disable each control you wish to update.

Let’s try dispabling the whole TForm. Note, this does not close flicker because EWB doesn’t update the form until you complete your operations.

procedure TForm1.LongOperationFast2;
var
  i : integer;
begin
  // disable the TForm
  Form1.Visible := False;

  MultiLineEdit1.Lines.Clear;
  for i := 0 to 2000 do
     MultilineEdit1.Lines.Add('line ' + IntToStr( i ));

  // Re-enable the form
  Form1.Visible := True;
end;

This executes in 0.131 seconds, which is roughly the same speed, but much more convenient because we can update anything on the entire form.

The final version doesn’t upate the TMultiLineEdit directly. It builds the TStringList then assigns.

procedure TForm1.LongOperationFast3;
var
  i : integer;
  sl : TStringList;
begin
  sl := TStringList.Create;

  for i := 0 to 2000 do
     sl.Add('line ' + IntToStr( i ));

  MultiLineEdit1.Lines.Assign( sl );
end;

This executes in 0.001 second, or a 4,000 times speed increase over the original solution.

Speeding up TDataSet Operations

I coverred the TDataSet speedup solution in an earlier article. Check it out.

Showing Busy Mouse

It’s a good practice to use the ShowProgress routine to indicate when the computer is busy.

But calling:

   ShowProgress('busy');
   LongOperationSlow;
   HideProgress;

doesn’t work, because EWB doesn’t update the mouse icon until it exits the current event.

The trick is to use the async keyword and schedule your slow operations.

Eg.

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowProgress('Busy');
  async CallProcedures;
  async HideProgress;

end;
procedure TForm1.CallProcedures;
var
  seconds : double;
begin
  ShowProgress('working');


  seconds := TimeThis( LongOperationSlow );
  label1.Caption := FloatToStr( seconds ) + ' s';

  seconds := TimeThis( LongOperationFast );
  label2.Caption := FloatToStr( seconds ) + ' s';

  seconds := TimeThis( LongOperationFast2 );
  label3.Caption := FloatToStr( seconds ) + ' s';

  seconds := TimeThis( LongOperationFast3 );
  label4.Caption := FloatToStr( seconds ) + ' s';
  HideProgress;
end;

That works like a charm.

Here’s the output:

output

By the way, which browser and operating system makes a huge difference. Here were the same tests tried on a M1 MacBook.

Browser Time

Chrome

4.1 seconds

Firefox

2.1 seconds

Safari

1.1 seconds

IE (Windows Arm
under Parallels)

6.7 seconds