Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Memory leak? 4

Status
Not open for further replies.

andjem1

Technical User
Jul 16, 2007
16
GB
Hi everyone. I have an app that converts values in a text file (cnc gcode) displayed in a Richedit. The program works ok except that every time the program is run there is a noticeable drop in speed. Also the larger the file the worse the problem. i.e. 61000 lines take 6mins 30s and 87000 take 13mins 10s.

I suspect a memory leak somewhere, however I don't really understand what they are, which part of the code is responsible and what to do about it.

The 'guts' of the program are shown below with the two main functions. (thanks to whosrdaddy)
If someone could point me in the right direction I would appreciate it.
Thanks
Beginner (as if you hadn't guessed!) using the free Turbo Delphi.



Code:
procedure TForm1.btnRunClick(Sender: TObject);
var
   I, LineCount: Integer;
   OldValue, Line : String;
   Radius, Diameter : Extended;
    begin
      If HasRun = 1 Then
        begin
         Beep;
         Showmessage('Code is already converted. You must reopen file to convert with another setting.');
         exit;
        end
      else
         Radius := StrToFloat(EditRadius.Text);  // Get the Stock radius from user.
         Diameter := StrToFloat(EditRadius.Text) * 2;  // Convert to Diameter
         Linecount := Richedit1.Lines.count;  // save the amount of lines to variable 'Linecount'
         btnRun.Caption := 'Calculating'; // Change caption of Run button.

        for i := 0 to Richedit1.Lines.Count-1 do  //from line 0 to total number of lines less 1 do the following.
         begin
           Line := Richedit1.Lines[i]; // load current line which is a string to variable 'Line'
           //X to B conversion type
           if radioButton1.Checked = true then
             if ansicontainsstr (Line,'X') then
                if HasUnwanted(['Y','Z','F','H','D','S','M'],Line) then
                  begin
                  OldValue := GetXMove(Line);
                  NewValue := 'B' + Convert(OldValue,Radius);
                  end
                else
                  begin
                  OldValue := AnsiMidstr(Line,(ansipos('X',Line)),maxint);
                  NewValue := 'B' + Convert(OldValue,Radius);
                  end;
            //Y to A conversion type
            if radioButton2.Checked = true then
              if ansicontainsstr (Line,'Y') then
                if HasUnwanted(['Z','F','H','D','S','M'],Line) then
                  begin
                  OldValue := GetYMove(Line);
                  NewValue := 'A' + Convert(OldValue,Radius);
                  end
                else
                  begin
                  OldValue := AnsiMidstr(Line,(ansipos('Y',Line)),maxint);
                  NewValue := 'A' + Convert(OldValue,Radius);
                  end;
           //X to A conversion type
           if radioButton3.Checked = true then
             if ansicontainsstr (Line,'X') then
                if HasUnwanted(['Y','Z','F','H','D','S','M'],Line) then
                  begin
                  OldValue := GetXMove(Line);
                  NewValue := 'A' + Convert(OldValue,Radius);
                  end
                else
                  begin
                  OldValue := AnsiMidstr(Line,(ansipos('X',Line)),maxint);
                  NewValue := 'A' + Convert(OldValue,Radius);
                  end;
            //Y to B conversion type
            if radioButton4.Checked = true then
              if ansicontainsstr (Line,'Y') then
                if HasUnwanted(['Z','F','H','D','S','M'],Line) then
                  begin
                  OldValue := GetYMove(Line);
                  NewValue := 'B' + Convert(OldValue,Radius);
                  end
                else
                  begin
                  OldValue := AnsiMidstr(Line,(ansipos('Y',Line)),maxint);
                  NewValue := 'B' + Convert(OldValue,Radius);
                  end;
             NewValue := AnsiReplaceStr(Line,(OldValue), (NewValue)); //Swap the old X or Y value with the new A or B value.
             Richedit1.Lines[i] := NewValue; // write converted line back into memo
             Progressbar1.Position := Trunc(i/Linecount*105);
     end;
          //Apply comments to the start of the converted program.
           richedit1.selstart := 0;
           richedit1.seltext := '(PROGRAM CONVERTED WITH WRAPPER)'+#13#10;
           richedit1.seltext := '('+ DateTimeToStr(Now) + ')'+#13#10;
             if radioButton1.Checked = true then
               begin
               richedit1.seltext := '(X AXIS MOVES CONVERTED TO ROTATIONAL B AXIS)'+#13#10;
               end;
             if radioButton2.Checked = true then
               begin
               richedit1.seltext := '(Y AXIS MOVES CONVERTED TO ROTATIONAL A AXIS)'+#13#10;
               end;
             if radioButton3.Checked = true then
               begin
               richedit1.seltext := '(X AXIS MOVES CONVERTED TO ROTATIONAL A AXIS)'+#13#10;
               end;
             if radioButton4.Checked = true then
               begin
               richedit1.seltext := '(Y AXIS MOVES CONVERTED TO ROTATIONAL B AXIS)'+#13#10;
               end;
     if i = Linecount then
       begin
        if radioButton1.Checked = true then
           showmessage('          X to B axis'+#13#10 +'conversion complete.');
        if radioButton2.Checked = true then
           showmessage('          Y to A axis'+#13#10 +'conversion complete.');
        if radioButton3.Checked = true then
           showmessage('          X to A axis'+#13#10 +'conversion complete.');
        if radioButton4.Checked = true then
           showmessage('          Y to B axis'+#13#10 +'conversion complete.');
      end;
      btnRun.Caption := 'Conversion Complete';
      HasRun := 1;   //Setting HasRun variable to 1 prevents converting the code twice.
      EditRadius.Enabled := False; // Code is already converted so can't be run again.
end;

function GetXMove(Str : string) : String;
var
    Index : Integer;
    Ch : Char;
    StrLen, XPos : Integer;
    NumStr : string;
         begin
              Result := '';               // Make sure result is empty first.
              StrLen := Length(Str);      // Get the length of the string passed to the function.
            if StrLen = 0 then Exit;      // Sanity Check
              Index := 0;
              NumStr := '';
              XPos := ansipos('X',Str)-1; // get position of X in the string
              Index := XPos;              // Give X position to Index.
            repeat
              Inc(Index);                 // Increment Index. Move along the string 1 position every repeat
              Ch := Str[Index];           // At every position get that character and pass it to variable ch
            case Ch of         // valid char. Ch is checked against wanted chars.
              'X','-','0'..'9','.' : NumStr := NumStr + Ch; //every wanted char is added to string held in variable 'NumStr'
          else
            begin
              Result := NumStr;
              Index := MaxInt;
            end;
        end;
            until
              Index >= StrLen;
end;

Function Convert (Value:string; var radius : extended) :string;
var
   Numbers, angle: Extended;
   formated: String;
   begin
     result := '';
       numbers := strtofloat (ansimidstr (value, 2, maxint));
       Angle := (numbers)*180/Pi/(Radius);
       Formated := Format('%.3f', [Angle]); //Format angle to 3 decimal places.
       Result := Formated;
end;
 
Forgive me as I haven't read through the post thoroughly but typically when I'm working with nasty data like this I'll work with an in memory database (or a real database if I think I'm dealing with too much data).

I just finished a database application that had to figure out how many oil wells were within a five mile radius of a point given in Lat/Long. The math was beyond me so an engineer figure it out (including taking the curvature of the earth into account) and I converted it to Delphi. To make a short story long I converted all lat/long values into integers in a database to keep thinks moving fast.

This also allows you the freedom of being able to mark each record as being processed so that you can pause the process and then continue it later on.
 
Glenn9999, Yes I would be very interested to see how you'd approach this.

DjangMan, When I said I'm just a beginner I wasn't kidding! I've not looked at database use yet, although the next project I have in mind will. So I'll keep your suggestion in mind. Thanks
 
Glenn9999, Yes I would be very interested to see how you'd approach this.

Here you go. YMMV of course. For what I could tell, though, it produced the same results on the data posted here that your program did. No guarantees though, but you should be able to compare with what was posted and learn a little bit.

Some comments: Part of what I wanted to do from my perspective is to see what I would find in terms of performance. While the string functions were indeed a major issue along with what came to light when the code was cleaned up, the thing I learned was really how much of a hit on performance that calling functions can put on a system.

To use an example, StrUtils has AnsiMidStr, which calls MidStr, which calls Copy. All have the same parms. Replacing AnsiMidStr with Copy decreased the execution time about 1.5 sec. For the profiler, this kind of hit shows itself by pinpointing the "begin" or "end" of the procedure or function.

But I will say in general that the profiler really does a wonderful job in pinpointing the exact lines where most of the time is being spent, along with what takes the most time in general.

----------
Those who work hard are rewarded with more work and remembered come time to downsize. Those who hardly work are given a paycheck and ignored completely.
 

Glenn, well I must say, I'm very impressed.

With Wrapper 20,000 lines took 33 seconds, with your code only 150 milliseconds, and a file with just over half a million lines (10.7 mb) took just 1909ms !

but you should be able to compare with what was posted and learn a little bit.

I disagree, with the help you've given I believe I'll learn an awful lot.

To use an example, StrUtils has AnsiMidStr, which calls MidStr, which calls Copy.

I'm beginning to see what you mean, the method I had adopted was overbloated, each small stage simply stacked one on top of another. The end result, while just about workable, was very inefficient.

It will take me a while to understand how you have simplified the code to such an extent.
I have one last question if you don't mind. I don't understand the following line of code, I get the idea of specifying an external buffer but not the use of an array and where did 65536 come from?

Code:
 tbuf1, tbuf2: array[1..65536] of char;

Thanks once again, and another star.

 
where did 65536 come from?

actually this 64 Kb (or 64 * 1024)

the largest value for a word (= 2 Bytes) is $FFFF hex or 65535 bytes

Glenn, another splendid thread, have a star!

/Daddy


-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
but not the use of an array and where did 65536 come from?

Whosrdaddy explained it, pretty much. Those two definitions are expanded buffers for the text files (TextFile defaults to 8K I believe). When you have I/O it is better to do it in bigger chunks, so more may be read in each time. Disks work in 512 byte units, so you want to define buffers that are some multiple of that. I find most times that 64K works best, so that's what I went with and it made a difference of about 2 seconds on 5,000,000 records.

----------
Those who work hard are rewarded with more work and remembered come time to downsize. Those who hardly work are given a paycheck and ignored completely.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top