[Back to UNITINFO SWAG index]  [Back to Main SWAG index]  [Original]

Unit Profiler; {*********************************************************}
               {*                                                       *}
               {*  PROFILER.PAS, 1997, Ralf Rosenkranz, Hagen, Germany  *}
               {*                                                       *}
               {*  This unit will help you to speed up your Programs.   *}
               {*  The source is PUBLIC DOMAIN, feel free to use it.    *}
               {*                                                       *}
               {*  It works with BP 7.0 and DOS (No Multitasking Env.)  *)
               {*                                                       *}
               {*  USAGE: See the example at the end of this file.      *}
               {*  Just place the PROFILER-Control-Lines in your Code,  *}
               {*  set "Options/Conditional defines" to PROFILE,        *}
               {*  and rebuild the Project. PROFILER will now generate  *}
               {*  at runtime a Timing-Profile of your Code, the        *}
               {*  result is in PROFILE.TXT. Attention: PROFILER uses   *}
               {*  a lot of CPU-time by itself, but this doesn't effekt *}
               {*  the result.                                          *}
               {*                                                       *}
               {*  Please visit my Homepage for more details:           *}
               {*  http://privat.schlund.de/RosenkranzRalf/RR01Home.html*}
               {*                                                       *}
               {*********************************************************}


INTERFACE


uses TpTimer;  {*********************************************************}
               {*                        uses:                          *}
               {*                                                       *}
               {*                   TPTIMER.PAS 2.00                    *}
               {*                by TurboPower Software                 *}
               {*                                                       *}
               {*         It's in the SWAG-Archive ! (TIMING.SWG)       *}
               {*********************************************************}


Type PSPT = ^PST;
     PST = Record
            H :Word;
            Name :String [64];
           end;


Procedure ProfilerEnterSection (SectionPtr :PSPT);

Procedure ProfilerLeaveSection (SectionPtr :PSPT);

Procedure ProfilerReport (FileName :String);


IMPLEMENTATION


{$ifdef DPMI}
const MaxSectionCount = 256;
{$else}
const MaxSectionCount = 64;
{$endif}

const LevelSpacer = '  ';

Type SSDPT = ^SSDT;
     SSDT = Record
             SAC :LongInt;
             SAMS :LongInt;
            end;

Type SSDPAPT = ^SSDPAT;
     SSDPAT = Array [1..MaxSectionCount] of SSDPT;

Type SSCT = Record
             SSAF :Boolean;
             SSDPAP :SSDPAPT;
            end;

Type SDT = Record
            CSP : PSPT;
            TAC :LongInt;
            TAMS :LongInt;
            ET :LongInt;
            LT :LongInt;
            CSH :Word;
            SAF :Boolean;
            SSC :SSCT;
            SOUPOTST :Real;
           end;

Type SDAPT = ^SDAT;
     SDAT = Array [1..MaxSectionCount] of SDT;

Type SCT = Record
            UC :Word;
            SDAP :SDAPT;
            CRSH :Word;
            RDC :LongInt;
           end;

var SC :SCT;

var THSF :Boolean;
    TAS :LongInt;
    TD :LongInt;
    TZO :LongInt;

var R :Text;
    HRF :Boolean;



Procedure Error (E :String);

begin
 WriteLn (E);
 Halt (1);
end;



Function IntToStr (L :LongInt) :String;

var Z :String;

begin
 Str (L, Z);
 IntToStr:= Z;
end;



Function RealToStr (R :Real) :String;

const MAS = 10000000;
      MIS = 0.0000001;

var Z :String;
    EF :Boolean;
    c :Char;

begin
   if ((Abs (R) < MAS) and (Abs (R) > MIS)) or (R = 0) then
   begin
      Str (R:17:16, Z);
      EF:= False;
      while not EF do
      begin
         c:= Z [Length (Z)];
         if (c = '0') or (c = '.')
         then Z:= Copy (Z, 1, Length (Z) - 1)
         else EF:= True;
         if (c = '.') or (Length (Z) <= 1) then EF:= True;
      end;
      while Z [1] = ' ' do Z:= Copy (Z, 2, Length (Z) - 1);
   end
   else
   begin
      Str (R, Z);
      while Z [1] = ' ' do Z:= Copy (Z, 2, Length (Z) - 1);
   end;
   RealToStr:= Z;
end;



Function FixRealStr (S :String; VKB, NKB :Integer) :String;

var PP :Byte;
    EP :Byte;
    MEF :Boolean;
    VK, NK, NE :String;

begin
 EP:= Pos ('E', S);
 if EP = 0 then EP:= Pos ('e', S);
 if EP = 0 then
 begin
  EP:= Length (S) + 1;
  MEF:= False;
 end
 else MEF:= True;
 PP:= Pos ('.', S);
 if (PP > 0) and
  (PP < EP) then
 begin
  VK:= Copy (S, 1, PP - 1);
  NK:= Copy (S, PP + 1, EP - (PP + 1));
  NE:= Copy (S, EP + 1, Length (S) - EP);
  if VK [1] = '-' then
  begin
   while ((VK [2] = '0') or
          (VK [2] = ' ')) and
          (Length (VK) > 2) do VK:= Copy (VK, 3, Length (VK) - 2);
  end
  else
  begin
   while ((VK [1] = '0') or
          (VK [1] = ' ')) and
          (Length (VK) > 1) do VK:= Copy (VK, 2, Length (VK) - 1);
  end;
  while Length (VK) < VKB do VK:= ' ' + VK;
  NK:= Copy (NK, 1, NKB);
  if MEF = True then
  begin
   while Length (NK) < NKB do NK:= ' ' + NK;
  end;
  if MEF = False
  then FixRealStr:= VK + '.' + NK
  else FixRealStr:= VK + '.' + NK + 'E' + NE;
 end
 else
 begin
  VK:= Copy (S, 1, EP - 1);
  NE:= Copy (S, EP + 1, Length (S) - EP);
  if VK [1] = '-' then
  begin
   while ((VK [2] = '0') or
          (VK [2] = ' ')) and
          (Length (VK) > 2)do VK:= Copy (VK, 3, Length (VK) - 2);
  end
  else
  begin
   while ((VK [1] = '0') or
          (VK [1] = ' ')) and
          (Length (VK) > 1) do VK:= Copy (VK, 2, Length (VK) - 1);
  end;
  while Length (VK) < VKB do VK:= ' ' + VK;
  if MEF = False
  then FixRealStr:= VK + '.' + '0'
  else FixRealStr:= VK + '.' + '0' + 'E' + NE;
 end;
end;



Procedure Init;

const InitSection :PST = (H:0; Name:'ProfilerInitSection');
const ZeroSection :PST = (H:0; Name:'ProfilerZeroSection');

const SLC = 1000;

var n,m :Word;

begin
 HRF:= False;
 THSF:= False;
 TAS:= 0;
 TD:= 0;
 with SC do
 begin
  UC:= 0;
  new (SDAP);
  CRSH:= 0;
  RDC:= 0;
  for n:= 1 to MaxSectionCount do
  begin
   with SDAP^[n] do
   begin
    CSP:= NIL;
    TAC:= 0;
    TAMS:= 0;
    ET:= 0;
    LT:= 0;
    CSH:= 0;
    SAF:= False;
    with SSC do
    begin
     New (SSDPAP);
     SSAF:= False;
     for m:= 1 to MaxSectionCount do
     begin
      SSDPAP^[m]:= NIL;
     end;
    end;
   end;
  end;
 end;
 TZO:= 0;
 for n:= 1 to SLC do
 begin
  ProfilerEnterSection (@InitSection);
  ProfilerLeaveSection (@InitSection);
 end;
 with SC.SDAP^[InitSection.H] do
 begin
  TZO:= Round (TAMS / TAC);
 end;
 for n:= 1 to SLC do
 begin
  ProfilerEnterSection (@ZeroSection);
  ProfilerLeaveSection (@ZeroSection);
 end;
end;



Procedure Done;

begin
 if HRF = False then ProfilerReport ('PROFILE.TXT');
end;



Procedure StopTime;

begin
 if THSF = True
 then Error ('Profiler.StopTime: Time is not running !');
 TAS:= ReadTimer - TD;
 THSF:= True;
end;



Procedure ContTime;

begin
 if THSF = False
 then Error ('Profiler.ContTime: Time has not been stopped !');
 Inc (TD, (ReadTimer - TD) - TAS);
 THSF:= False;
end;



Function ReadMicroSecTime :LongInt;

begin
 if THSF = True
 then ReadMicroSecTime:= TAS
 else ReadMicroSecTime:= ReadTimer - TD;
end;



Procedure ProfilerEnterSection (SectionPtr :PSPT);

var H :Word;

begin
 StopTime;
 if SC.RDC > 0 then
 begin
  Inc (SC.RDC);
 end
 else
 begin
  H:= SectionPtr^.H;
  if H = 0 then
  begin
   with SC do
   begin
    if UC >= MaxSectionCount
    then Error ('ProfilerEnterSection: Limit: ' +
          IntToStr (MaxSectionCount) + 'Sections  !');
    Inc (UC);
    H:= UC;
    SectionPtr^.H:= H;
    with SDAP^[H] do
    begin
     CSP:= SectionPtr;
     TAC:= 1;
     TAMS:= 0;
     ET:= ReadMicroSecTime;
     LT:= 0;
     CSH:= CRSH;
     CRSH:= H;
     SAF:= True;
    end;
   end;
  end
  else
  begin
   with SC do
   begin
    with SDAP^[H] do
    begin
     if SAF = True then
     begin
      SC.RDC:= 1;
     end
     else
     begin
      Inc (TAC);
      ET:= ReadMicroSecTime;
      CSH:= CRSH;
      CRSH:= H;
      SAF:= True;
     end;
    end;
   end;
  end;
 end;
 ContTime;
end;



Procedure ProfilerLeaveSection (SectionPtr :PSPT);

var H :Word;
    DMS :LongInt;

begin
 StopTime;
 if SC.RDC > 0 then
 begin
  Dec (SC.RDC);
 end
 else
 begin
  H:= SectionPtr^.H;
  if H <> SC.CRSH then
   with SC do
    Error ('ProfilerLeaveSection: LeaveSection ' +
        SectionPtr^.Name +
        ' doesn''t match EnterSection ' +
        SDAP^[CRSH].CSP^.Name);
  with SC do
  begin
   with SDAP^[H] do
   begin
    LT:= ReadMicroSecTime;
    DMS:= LT - ET;
    DMS:= DMS - TZO;
    Inc (TAMS, DMS);
    CRSH:= CSH;
    SAF:= False;
   end;
   if CRSH > 0 then
   begin
    with SDAP^[CRSH] do
    begin
     with SSC do
     begin
      if SSDPAP^[H] = NIL then
      begin
       New (SSDPAP^[H]);
       SSAF:= True;
       with SSDPAP^[H]^ do
       begin
        SAC:= 1;
        SAMS:= DMS;
       end;
      end
      else
      begin
       with SSDPAP^[H]^ do
       begin
        Inc (SAC);
        Inc (SAMS, DMS);
       end;
      end;
     end;
    end;
   end;
  end;
 end;
 ContTime;
end;



Procedure SectionReport (H :Word);

var SH :Word;
    n :Integer;
    L :Word;
    AVMS :Real;
    FP :Real;

begin
 with SC.SDAP^[H] do
 begin
  AVMS:= TAMS / TAC;
  Write (R, CSP^.Name, ':');
  Write (R, ' AverageMicroSecs=', FixRealStr (RealToStr (AVMS), 1, 1));
  Write (R, ' ActiveMicroSecs=', TAMS);
  Write (R, ' ActiveCount=', TAC);
  WriteLn (R);
 end;
end;



Procedure SubSectionReport (L :Word;
                            H :Word;
                            CSAMS :Real;
                            ACPOTST:Real;
                            CSAC :LongInt;
                            TSAC :LongInt;
                            TSAMS :LongInt;
                            PF :Boolean);

var SH :Word;
    n :Integer;
    TSN :String;
    TSAVMS :Real;
    TSCPCS :Real;
    TSEMS :Real;
    AMS :Real;
    SSCPTS :Real;
    EMS :Real;
    ASSEMS :Real;
    NISSEMS :Real;
    LPOTST :Real;
    UPOTST:Real;

begin
 with SC.SDAP^[H] do
 begin
  TSN:= CSP^.Name;
  ASSEMS:= 0;
  with SSC do
  begin
   for SH:= 1 to SC.UC do
   begin
    if SSDPAP^[SH] <> NIL then
    begin
     with SSDPAP^[SH]^ do
     begin
      AMS:= SAMS / SAC;
      SSCPTS:= SAC / TSAC;
      EMS:= AMS * SSCPTS;
      ASSEMS:= ASSEMS + EMS;
     end;
    end;
   end;
  end;
 end;
 TSCPCS:= TSAC / CSAC;
 TSAVMS:= TSAMS / TSAC;
 TSEMS:= TSAVMS * TSCPCS;
 NISSEMS:= (TSAVMS - ASSEMS) * TSCPCS;
 if NISSEMS < 0 then NISSEMS:= 0;
 LPOTST:= ACPOTST * (TSEMS / CSAMS);
 UPOTST:= ACPOTST * (NISSEMS / CSAMS);
 SC.SDAP^[H].SOUPOTST:= SC.SDAP^[H].SOUPOTST + UPOTST;
 if PF = True then
 begin
  for n:= 1 to L do Write (R, LevelSpacer);
  Write (R, TSN, ':');
  Write (R, ' (Level)\Used%OfTime=',
            '(', FixRealStr (RealToStr (LPOTST), 1, 1), ')\',
            FixRealStr (RealToStr (UPOTST), 1, 1), '%');
  Write (R, ' EffectiveMicroSecs=', FixRealStr (RealToStr (NISSEMS), 1, 1));
  Write (R, ' ActiveMicroSecs=', TSAMS);
  Write (R, ' ActiveCount=', TSAC);
  WriteLn (R);
 end;
 with SC.SDAP^[H].SSC do
 begin
  for SH:= 1 to SC.UC do
  begin
   if SSDPAP^[SH] <> NIL then
   begin
    with SSDPAP^[SH]^ do
    begin
     SubSectionReport (L + 1, SH, TSAVMS, LPOTST, TSAC, SAC, SAMS, PF);
    end;
   end;
  end;
 end;
end;



Procedure TopSectionReport (H :Word; PF :Boolean);

var SH :Word;
    n :Integer;
    L :Word;
    AMS :Real;
    FP :Real;
    CAC :LongInt;

begin
 with SC do
 begin
  for n:= 1 to UC do
  begin
   SDAP^[n].SOUPOTST:= 0;
  end;
 end;
 with SC.SDAP^[H] do
 begin
  L:= 0;
  AMS:= TAMS / TAC;
  FP:= 100;
  CAC:= TAC;
  if PF = True then
  begin
   Write (R, CSP^.Name, ':');
   Write (R, ' AvailPercentOfTime=100.0%');
   Write (R, ' AverageMicroSecs=', FixRealStr (RealToStr (AMS), 1, 1));
   Write (R, ' ActiveMicroSecs=', TAMS);
   Write (R, ' ActiveCount=', TAC);
   WriteLn (R);
  end;
  with SSC do
  begin
   for SH:= 1 to SC.UC do
   begin
    if SSDPAP^[SH] <> NIL then
    begin
     with SSDPAP^[SH]^ do
     begin
      SubSectionReport (L + 1, SH, AMS, FP, CAC, SAC, SAMS, PF);
     end;
    end;
   end;
  end;
 end;
end;



Procedure ProfilerReport (FileName :String);

var H :Word;
    SH :Word;
    n :Integer;
    SOA :Real;

begin
 StopTime;
 Assign (R, FileName);
 Rewrite (R);
 with SC do
 begin
  Writeln (R);
  WriteLn (R, '--- Section Overview ---------------------------------------------------------');
  Writeln (R);
  for H:= 1 to UC do
  begin
   SectionReport (H);
  end;
  for n:= 1 to 5 do WriteLn (R);
  Writeln (R);
  WriteLn (R, '--- Top-Level-Sections Tree-View ---------------------------------------------');
  Writeln (R);
  for H:= 1 to UC do
  begin
   if SDAP^[H].CSH = 0 then
   begin
    TopSectionReport (H, True);
    WriteLn (R);
   end;
  end;
  for n:= 1 to 4 do WriteLn (R);
  Writeln (R);
  WriteLn (R, '--- Sub-Level-Sections Tree-View ---------------------------------------------');
  Writeln (R);
  for H:= 1 to UC do
  begin
   if (SDAP^[H].CSH > 0) and
    (SDAP^[H].SSC.SSAF = True) then
   begin
    TopSectionReport (H, True);
    WriteLn (R);
   end;
  end;
  for n:= 1 to 4 do WriteLn (R);
  Writeln (R);
  WriteLn (R, '--- Top-Level-Sections Flat-View ---------------------------------------------');
  Writeln (R);
  for H:= 1 to UC do
  begin
   if SDAP^[H].CSH = 0 then
   begin
    TopSectionReport (H, False);
    with SDAP^[H] do
    begin
     WriteLn (R, CSP^.Name, ':');
    end;
    SOA:= 0;
    for SH:= 1 to UC do
    begin
     if SDAP^[SH].SOUPOTST > 0 then
     begin
      with SDAP^[SH] do
      begin
       Write (R, LevelSpacer);
       Write (R, CSP^.Name, ':');
       Write (R, ' Time%=', FixRealStr (RealToStr (SOUPOTST), 1, 1));
       WriteLn (R);
       SOA:= SOA + SOUPOTST;
      end;
     end;
    end;
    WriteLn (R, LevelSpacer + FixRealStr (RealToStr (SOA), 1, 1), '% of Time used in Sections');
    WriteLn (R);
   end;
  end;
  for n:= 1 to 4 do WriteLn (R);
  Writeln (R);
  WriteLn (R, '--- Sub-Level-Sections Flat-View ---------------------------------------------');
  Writeln (R);
  for H:= 1 to UC do
  begin
   if (SDAP^[H].CSH > 0) and
    (SDAP^[H].SSC.SSAF = True) then
   begin
    TopSectionReport (H, False);
    with SDAP^[H] do
    begin
     WriteLn (R, CSP^.Name, ':');
    end;
    SOA:= 0;
    for SH:= 1 to UC do
    begin
     if SDAP^[SH].SOUPOTST > 0 then
     begin
      with SDAP^[SH] do
      begin
       Write (R, LevelSpacer);
       Write (R, CSP^.Name, ':');
       Write (R, ' Time%=', FixRealStr (RealToStr (SOUPOTST), 1, 1));
       WriteLn (R);
       SOA:= SOA + SOUPOTST;
      end;
     end;
    end;
    WriteLn (R, LevelSpacer + FixRealStr (RealToStr (SOA), 1, 1), '% of Time used in Sections');
    WriteLn (R);
   end;
  end;
  for n:= 1 to 4 do WriteLn (R);
 end;
 Close (R);
 HRF:= True;
 ContTime;
end;



var ESP :Pointer;



Procedure UnitExit; FAR;

begin
 ExitProc:= ESP;
 Done;
end;



begin
 Init;
 ESP:= ExitProc;
 ExitProc:= @UnitExit;
end.




### snip ##########################################################################################


Program PROFTEST;

{$define PROFILE}

uses {$ifdef PROFILE} Profiler, {$endif} DOS;



Procedure WasteTime (Count :Word);

var n,m :Word;
    Dummy :Real;

begin
   for n:= 1 to Count do
   begin
      for m:= 1 to 10 do
      begin
         Dummy:= Sin ((m/10)*PI*2);
      end;
   end;
end;




Procedure Proc_1;

{$ifdef PROFILE} const Section :PST = (H:0; Name:'Proc_1'); {$endif}

begin
   {$ifdef PROFILE} ProfilerEnterSection (@Section); {$endif}

   WriteLn ('Proc_1');
   WasteTime (100);

   {$ifdef PROFILE} ProfilerLeaveSection (@Section); {$endif}
end;




Procedure Proc_2;

{$ifdef PROFILE} const Section :PST = (H:0; Name:'Proc_2'); {$endif}

var n :Integer;

begin
   {$ifdef PROFILE} ProfilerEnterSection (@Section); {$endif}

   WriteLn ('Proc_2');
   WasteTime (200);

   for n:= 1 to 10 do Proc_1;

   {$ifdef PROFILE} ProfilerLeaveSection (@Section); {$endif}
end;




{$ifdef PROFILE} const Section :PST = (H:0; Name:'MainLoop'); {$endif}

var n :Integer;

begin
   {$ifdef PROFILE} ProfilerEnterSection (@Section); {$endif}

   WriteLn ('Start');

   for n:= 1 to 4 do
   begin
      WriteLn (n);

      Proc_1;
      Proc_2;
   end;

   WriteLn ('Stop');
   WriteLn;
   WriteLn ('Results in PROFILE.TXT');
   WriteLn;

   {$ifdef PROFILE} ProfilerLeaveSection (@Section); {$endif}
end.





### snip ##########################################################################################



Result: PROFILE.TXT



--- Section Overview ---------------------------------------------------------

ProfilerInitSection: AverageMicroSecs=24.9 ActiveMicroSecs=24950 ActiveCount=1000
ProfilerZeroSection: AverageMicroSecs=0.2 ActiveMicroSecs=229 ActiveCount=1000
MainLoop: AverageMicroSecs=873098.0 ActiveMicroSecs=873098 ActiveCount=1
Proc_1: AverageMicroSecs=16450.2 ActiveMicroSecs=723809 ActiveCount=44
Proc_2: AverageMicroSecs=196836.5 ActiveMicroSecs=787346 ActiveCount=4






--- Top-Level-Sections Tree-View ---------------------------------------------

ProfilerInitSection: AvailPercentOfTime=100.0% AverageMicroSecs=24.9 ActiveMicroSecs=24950 ActiveCount=1000

ProfilerZeroSection: AvailPercentOfTime=100.0% AverageMicroSecs=0.2 ActiveMicroSecs=229 ActiveCount=1000

MainLoop: AvailPercentOfTime=100.0% AverageMicroSecs=873098.0 ActiveMicroSecs=873098 ActiveCount=1
  Proc_1: (Level)\Used%OfTime=(7.5)\7.5% EffectiveMicroSecs=65766.0 ActiveMicroSecs=65766 ActiveCount=4
  Proc_2: (Level)\Used%OfTime=(90.1)\14.8% EffectiveMicroSecs=129303.0 ActiveMicroSecs=787346 ActiveCount=4
    Proc_1: (Level)\Used%OfTime=(75.3)\75.3% EffectiveMicroSecs=164510.7 ActiveMicroSecs=658043 ActiveCount=40






--- Sub-Level-Sections Tree-View ---------------------------------------------

Proc_2: AvailPercentOfTime=100.0% AverageMicroSecs=196836.5 ActiveMicroSecs=787346 ActiveCount=4
  Proc_1: (Level)\Used%OfTime=(83.5)\83.5% EffectiveMicroSecs=164510.7 ActiveMicroSecs=658043 ActiveCount=40






--- Top-Level-Sections Flat-View ---------------------------------------------

ProfilerInitSection:
  0.0% of Time used in Sections

ProfilerZeroSection:
  0.0% of Time used in Sections

MainLoop:
  Proc_1: Time%=82.9
  Proc_2: Time%=14.8
  97.7% of Time used in Sections






--- Sub-Level-Sections Flat-View ---------------------------------------------

Proc_2:
  Proc_1: Time%=83.5
  83.5% of Time used in Sections






[Back to UNITINFO SWAG index]  [Back to Main SWAG index]  [Original]