Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi loop speed question

Is there a faster way? I basically need to add AA-ZZ to thousands of records at a time.

Just a list of 35 items takes quite a while to complete muchless a list of a thousand.


procedure Tmainform.btnSeederClick(Sender: TObject);
var
  ch,ch2:char;
  i:integer;
  slist1, slist2:TStrings;
begin
  slist1:= TStringList.Create;
  slist2:= TStringList.Create;
  slist1.Text :=queuebox.Items.Text;
  for ch := 'a' to 'z' do
    begin
      for ch2 := 'a' to 'z' do
        begin
          //

      for I := 0 to slist1.Count - 1 do
        begin
        application.ProcessMessages; // so it doesn't freeze the application in long loops.  Not 100% sure where this should be placed, if at all.
         sleep(1);  //Without this it doesn't process the cancel button.
         if cancel then Break; 
         slist2.Add(slist1.Strings[i]+ch+ch2);
        end;
    end;
end;
insertsingle(slist2,queuebox);
freeandnil(slist1);
freeandnil(slist2);

end;

Thanks for any help

like image 646
Brad Avatar asked Jan 20 '10 04:01

Brad


2 Answers

There are a couple obvious problems with your code.

First off, you're wasting a lot of CPU cycles computing the same values over and over again. The AA..ZZ values aren't going to change, so there's no need to build them over and over. Try something like this: Create a third TStringList. Go through and fill it with all possible AA..ZZ permutations with your double loop. Once that's over with, loop through and merge this list of precomputed strings with the values in slist1. You should see a pretty big boost from that.

(Or, if time is absolutely at a premium, write a minor little program that will compute the permutation list and save it to a textfile, then compile that into your app as a string resource which you can load at runtime.)

Second, and this is probably what's killing you, you shouldn't have the ProcessMessages and the Sleep calls in the innermost loop. Sleep(1); sounds like it means "sleep for 1 milisecond", but Windows doesn't offer that sort of precision. What you end up getting is "sleep for at least 1 milisecond". It releases the CPU until Windows gets back around to it, which is usually somewhere on the order of 16 miliseconds. So you're adding a delay of 16 msec (plus as long as ProcessMessages takes) into a very tight loop that probably takes only a few microseconds to execute the rest of its code.

If you need something like that to keep the UI responsive, it should be in the outermost loop, not an inner one, and you probably don't even need to run it every iteration. Try something like if ch mod 100 = 0 then //sleep and process messages here. Craig's suggestion to move this task to a worker thread would also help, but only if you know enough about threads to get it right. They can be tricky.

like image 60
Mason Wheeler Avatar answered Oct 11 '22 13:10

Mason Wheeler


You should surround your code with slist2.BeginUpdate() and slist2.EndUpdate(), to stop TStringList from doing extra processing.

From my experience, you would get a very large improvement by using fewer ProcessMessages(); Sleep(1); statements, as suggested in other answers.

Try moving it to just below the first for loop, and see what improvement you get.

like image 43
Blorgbeard Avatar answered Oct 11 '22 14:10

Blorgbeard