353 lines
8.5 KiB
ObjectPascal
353 lines
8.5 KiB
ObjectPascal
unit KM_PerfLog;
|
|||
|
|
{$I KaM_Remake.inc}
|
||
|
|
interface
|
||
|
|
uses
|
||
|
|
Generics.Collections
|
||
|
|
{$IFDEF FPC}
|
||
|
|
, KM_Sort
|
||
|
|
{$ENDIF}
|
||
|
|
;
|
||
|
|
|
||
|
|
|
||
|
|
type
|
||
|
|
TKMPerfSection = (pskTick,
|
||
|
|
pskHungarian, pskAIFields, pskFOW,
|
||
|
|
pskHands, pskPathfinding, pskTerrain, psGIP, pskScripting,
|
||
|
|
pskRender,
|
||
|
|
psFOWCheck, psFOWCheck3, psFOWCheck5,
|
||
|
|
psRenderTer,
|
||
|
|
psRenderTerBase, pskUpdateVBO, psDoTiles, psDoWater, psDoTilesLayers, psDoOverlays, psDoLighting, psDoShadows,
|
||
|
|
psRenderFences, psRenderPlans,
|
||
|
|
psRenderOther, psRenderList, psRenderHands, psRenderFOW
|
||
|
|
);
|
||
|
|
|
||
|
|
const
|
||
|
|
//Unicode for TStringList population
|
||
|
|
SectionName: array [TKMPerfSection] of UnicodeString = ('Tick', 'Hungur', 'AIFields', 'FOW',
|
||
|
|
'Hands', 'PFinding', 'Terrain', 'GIP', 'Script',
|
||
|
|
'Render',
|
||
|
|
'FOWChecks', 'FOWCheck3', 'FOWCheck5',
|
||
|
|
'RenderTer', 'RenTerBase',
|
||
|
|
'UpdateVBO', 'DoTiles', 'DoWater', 'DoLayers', 'DoOverlays', 'DoLight', 'DoShadows',
|
||
|
|
'RenFences', 'RenPlans',
|
||
|
|
'RenderOth', 'RendList', 'RendHands', 'RenderFOW'
|
||
|
|
);
|
||
|
|
|
||
|
|
const
|
||
|
|
SKIP_SECTION: set of TKMPerfSection = [pskHungarian, pskAIFields, pskFOW, psFOWCheck, psFOWCheck3, psFOWCheck5,
|
||
|
|
psRenderOther, psRenderList, psRenderHands, psRenderFOW,
|
||
|
|
pskHands, pskPathfinding, pskTerrain, psGIP, pskScripting];
|
||
|
|
|
||
|
|
type
|
||
|
|
//Log how much time each section takes and write results to a log file
|
||
|
|
|
||
|
|
TKMPerfSectionData = record
|
||
|
|
Section: TKMPerfSection;
|
||
|
|
Time: Int64;
|
||
|
|
end;
|
||
|
|
|
||
|
|
TKMPerfLog = class
|
||
|
|
private
|
||
|
|
fTick: Cardinal;
|
||
|
|
fRenderCounter: Cardinal;
|
||
|
|
fCount: array [TKMPerfSection] of Integer;
|
||
|
|
fTimeEnter: array [TKMPerfSection] of Int64;
|
||
|
|
// fTimes: array [TPerfSection] of array of Int64;
|
||
|
|
fTickTimes: TDictionary<Cardinal, TDictionary<TKMPerfSection, Int64>>;
|
||
|
|
// fRenderTimes: TDictionary<Cardinal, TDictionary<TKMPerfSection, Int64>>;
|
||
|
|
public
|
||
|
|
constructor Create;
|
||
|
|
destructor Destroy; override;
|
||
|
|
procedure Clear;
|
||
|
|
procedure StartTick(aTick: Cardinal);
|
||
|
|
procedure EndTick;
|
||
|
|
procedure StartRender;//(aCounter: Cardinal);
|
||
|
|
procedure EndRender;
|
||
|
|
procedure EnterSection(aSection: TKMPerfSection);
|
||
|
|
procedure LeaveSection(aSection: TKMPerfSection);
|
||
|
|
procedure SaveToFile(const aFilename: UnicodeString);
|
||
|
|
end;
|
||
|
|
|
||
|
|
|
||
|
|
implementation
|
||
|
|
uses
|
||
|
|
Classes, SysUtils, KM_CommonUtils;
|
||
|
|
|
||
|
|
|
||
|
|
const
|
||
|
|
SECT_W = 12;
|
||
|
|
|
||
|
|
|
||
|
|
// LIMITS: array[TPerfSection] of Integer = (30000, 5000, 5000, 5000, 5000, 5000, 5000, 5000);
|
||
|
|
|
||
|
|
|
||
|
|
{ TKMPerfLog }
|
||
|
|
constructor TKMPerfLog.Create;
|
||
|
|
var
|
||
|
|
K: TKMPerfSection;
|
||
|
|
begin
|
||
|
|
inherited;
|
||
|
|
|
||
|
|
for K := Low(TKMPerfSection) to High(TKMPerfSection) do
|
||
|
|
fTimeEnter[K] := 0;
|
||
|
|
|
||
|
|
fTickTimes := TDictionary<Cardinal, TDictionary<TKMPerfSection, Int64>>.Create;
|
||
|
|
|
||
|
|
fRenderCounter := 0;
|
||
|
|
end;
|
||
|
|
|
||
|
|
|
||
|
|
destructor TKMPerfLog.Destroy;
|
||
|
|
var
|
||
|
|
SectDict: TDictionary<TKMPerfSection, Int64>;
|
||
|
|
begin
|
||
|
|
for SectDict in fTickTimes.Values do
|
||
|
|
SectDict.Free;
|
||
|
|
|
||
|
|
fTickTimes.Clear;
|
||
|
|
fTickTimes.Free;
|
||
|
|
|
||
|
|
inherited;
|
||
|
|
end;
|
||
|
|
|
||
|
|
|
||
|
|
procedure TKMPerfLog.Clear;
|
||
|
|
var
|
||
|
|
I: TKMPerfSection;
|
||
|
|
begin
|
||
|
|
for I := Low(TKMPerfSection) to High(TKMPerfSection) do
|
||
|
|
fCount[I] := 0;
|
||
|
|
end;
|
||
|
|
|
||
|
|
|
||
|
|
procedure TKMPerfLog.StartTick(aTick: Cardinal);
|
||
|
|
var
|
||
|
|
K: TKMPerfSection;
|
||
|
|
SectDict: TDictionary<TKMPerfSection, Int64>;
|
||
|
|
begin
|
||
|
|
Assert(not fTickTimes.ContainsKey(aTick), 'Tick is already started');
|
||
|
|
|
||
|
|
fTick := aTick;
|
||
|
|
|
||
|
|
SectDict := TDictionary<TKMPerfSection, Int64>.Create;
|
||
|
|
fTickTimes.Add(fTick, SectDict);
|
||
|
|
|
||
|
|
EnterSection(pskTick);
|
||
|
|
|
||
|
|
//Init all sections
|
||
|
|
for K := Low(TKMPerfSection) to High(TKMPerfSection) do
|
||
|
|
begin
|
||
|
|
if K = pskTick then
|
||
|
|
Continue;
|
||
|
|
|
||
|
|
EnterSection(K);
|
||
|
|
LeaveSection(K);
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
|
||
|
|
procedure TKMPerfLog.EndTick;
|
||
|
|
begin
|
||
|
|
LeaveSection(pskTick);
|
||
|
|
end;
|
||
|
|
|
||
|
|
|
||
|
|
procedure TKMPerfLog.StartRender;//(aCounter: Cardinal);
|
||
|
|
var
|
||
|
|
K: TKMPerfSection;
|
||
|
|
SectDict: TDictionary<TKMPerfSection, Int64>;
|
||
|
|
begin
|
||
|
|
Inc(fRenderCounter);
|
||
|
|
// Assert(not fRenderTimes.ContainsKey(fRenderCounter), 'Tick is already started');
|
||
|
|
|
||
|
|
SectDict := TDictionary<TKMPerfSection, Int64>.Create;
|
||
|
|
fTickTimes.Add(fRenderCounter, SectDict);
|
||
|
|
|
||
|
|
EnterSection(pskRender);
|
||
|
|
|
||
|
|
//Init all sections
|
||
|
|
for K := Low(TKMPerfSection) to High(TKMPerfSection) do
|
||
|
|
begin
|
||
|
|
if K in [pskTick, pskRender] then
|
||
|
|
Continue;
|
||
|
|
|
||
|
|
EnterSection(K);
|
||
|
|
LeaveSection(K);
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
|
||
|
|
procedure TKMPerfLog.EndRender;
|
||
|
|
begin
|
||
|
|
LeaveSection(pskRender);
|
||
|
|
end;
|
||
|
|
|
||
|
|
|
||
|
|
procedure TKMPerfLog.EnterSection(aSection: TKMPerfSection);
|
||
|
|
begin
|
||
|
|
Assert(fTimeEnter[aSection] = 0, 'Entering not left section');
|
||
|
|
fTimeEnter[aSection] := TimeGetUsec;
|
||
|
|
end;
|
||
|
|
|
||
|
|
|
||
|
|
procedure TKMPerfLog.LeaveSection(aSection: TKMPerfSection);
|
||
|
|
var
|
||
|
|
T, OldT: Int64;
|
||
|
|
SectDict: TDictionary<TKMPerfSection, Int64>;
|
||
|
|
// SectData: TKMPerfSectionData;
|
||
|
|
begin
|
||
|
|
T := TimeGetUsec - fTimeEnter[aSection]; //Measure it ASAP
|
||
|
|
// if fCount[aSection] >= Length(fTimes[aSection]) then
|
||
|
|
// SetLength(fTimes[aSection], fCount[aSection] + 1024);
|
||
|
|
//
|
||
|
|
// fTimes[aSection, fCount[aSection]] := T;
|
||
|
|
// Assert(fTickTimes.ContainsKey(fTick), 'Leave unopened section at tick' + IntToStr(fTick));
|
||
|
|
|
||
|
|
if fTickTimes.TryGetValue(fTick, SectDict) then
|
||
|
|
// raise Exception.CreateFmt('Leave unopened section at tick %d', [fTick])
|
||
|
|
// else
|
||
|
|
begin
|
||
|
|
// SectData.Section := aSection;
|
||
|
|
// SectData.Time := T;
|
||
|
|
if SectDict.TryGetValue(aSection, OldT) then
|
||
|
|
SectDict.Items[aSection] := OldT + T
|
||
|
|
else
|
||
|
|
SectDict.Add(aSection, T);
|
||
|
|
end;
|
||
|
|
|
||
|
|
// Inc(fCount[aSection]);
|
||
|
|
|
||
|
|
fTimeEnter[aSection] := 0;
|
||
|
|
end;
|
||
|
|
|
||
|
|
{$IFDEF FPC}
|
||
|
|
function CompareTicks(const aKey1, aKey2): Integer;
|
||
|
|
var
|
||
|
|
k1: Cardinal absolute aKey1;
|
||
|
|
k2: Cardinal absolute aKey2;
|
||
|
|
begin
|
||
|
|
if k1 < k2 then Result := -1
|
||
|
|
else if k1 > k2 then Result := +1
|
||
|
|
else Result := 0;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function CompareSects(const aKey1, aKey2): Integer;
|
||
|
|
var
|
||
|
|
k1: TKMPerfSection absolute aKey1;
|
||
|
|
k2: TKMPerfSection absolute aKey2;
|
||
|
|
begin
|
||
|
|
if k1 < k2 then Result := -1
|
||
|
|
else if k1 > k2 then Result := +1
|
||
|
|
else Result := 0;
|
||
|
|
end;
|
||
|
|
{$ENDIF}
|
||
|
|
|
||
|
|
procedure TKMPerfLog.SaveToFile(const aFilename: UnicodeString);
|
||
|
|
var
|
||
|
|
I: Integer;
|
||
|
|
K: TKMPerfSection;
|
||
|
|
S: TStringList;
|
||
|
|
TickKey: Cardinal;
|
||
|
|
SectKey: TKMPerfSection;
|
||
|
|
Str: String;
|
||
|
|
SectDict: TDictionary<TKMPerfSection, Int64>;
|
||
|
|
|
||
|
|
FastTick: Boolean;
|
||
|
|
|
||
|
|
SectsArray: TArray<TKMPerfSection>;
|
||
|
|
TicksArray: TArray<Cardinal>;
|
||
|
|
|
||
|
|
Total: array[TKMPerfSection] of Int64;
|
||
|
|
begin
|
||
|
|
ForceDirectories(ExtractFilePath(aFilename));
|
||
|
|
|
||
|
|
S := TStringList.Create;
|
||
|
|
|
||
|
|
TicksArray := fTickTimes.Keys.ToArray;
|
||
|
|
{$IFNDEF FPC}
|
||
|
|
TArray.Sort<Cardinal>(TicksArray);
|
||
|
|
{$ELSE}
|
||
|
|
SortCustom(TicksArray, Low(TicksArray), High(TicksArray), SizeOf(TicksArray[0]), CompareTicks);
|
||
|
|
{$ENDIF}
|
||
|
|
|
||
|
|
// Str := 'Tick '; //7
|
||
|
|
Str := ' ';
|
||
|
|
for K := Low(TKMPerfSection) to High(TKMPerfSection) do
|
||
|
|
begin
|
||
|
|
if K in SKIP_SECTION then
|
||
|
|
Continue;
|
||
|
|
|
||
|
|
Total[K] := 0;
|
||
|
|
Str := Str + Format('%' + IntToStr(SECT_W) + 's', [SectionName[K]]);
|
||
|
|
end;
|
||
|
|
S.Append(Str);
|
||
|
|
|
||
|
|
|
||
|
|
I := 0;
|
||
|
|
for TickKey in TicksArray do
|
||
|
|
begin
|
||
|
|
Inc(I);
|
||
|
|
SectDict := fTickTimes.Items[TickKey];
|
||
|
|
|
||
|
|
SectsArray := SectDict.Keys.ToArray;
|
||
|
|
{$IFNDEF FPC}
|
||
|
|
TArray.Sort<TKMPerfSection>(SectsArray);
|
||
|
|
{$ELSE}
|
||
|
|
SortCustom(SectsArray, Low(SectsArray), High(SectsArray), SizeOf(SectsArray[0]), CompareSects);
|
||
|
|
{$ENDIF}
|
||
|
|
|
||
|
|
FastTick := False;
|
||
|
|
Str := Format('%6d:', [TickKey]);
|
||
|
|
for SectKey in SectsArray do
|
||
|
|
begin
|
||
|
|
if SectKey in SKIP_SECTION then
|
||
|
|
Continue;
|
||
|
|
// if (SectKey = psTick) and (SectDict.Items[SectKey] < 5000) then //Skip ticks with low execution time
|
||
|
|
// begin
|
||
|
|
// FastTick := True;
|
||
|
|
// Break;
|
||
|
|
// end;
|
||
|
|
|
||
|
|
Str := Str + Format('%' + IntToStr(SECT_W) + 'd', [SectDict.Items[SectKey]]);
|
||
|
|
|
||
|
|
if I < Length(TicksArray) then
|
||
|
|
Total[SectKey] := Total[SectKey] + SectDict.Items[SectKey];
|
||
|
|
end;
|
||
|
|
|
||
|
|
if not FastTick then
|
||
|
|
S.Append(Str);
|
||
|
|
end;
|
||
|
|
|
||
|
|
S.Append('AVERAGE');
|
||
|
|
Str := '';
|
||
|
|
for K := Low(TKMPerfSection) to High(TKMPerfSection) do
|
||
|
|
begin
|
||
|
|
if K in SKIP_SECTION then
|
||
|
|
Continue;
|
||
|
|
|
||
|
|
Str := Str + Format('%' + IntToStr(SECT_W) + 'd', [Round(Total[K] / Length(TicksArray))]);
|
||
|
|
end;
|
||
|
|
S.Append(Str);
|
||
|
|
|
||
|
|
// for K := Low(TPerfSection) to High(TPerfSection) do
|
||
|
|
// begin
|
||
|
|
// //Section name
|
||
|
|
// S.Append(SectionName[K]);
|
||
|
|
// S.Append(StringOfChar('-', 60));
|
||
|
|
//
|
||
|
|
// //Times
|
||
|
|
// for I := 0 to fCount[K] - 1 do
|
||
|
|
// if fTimes[K,I] > LIMITS[K] then //Dont bother saving 95% of data
|
||
|
|
// S.Append(Format('%d'#9'%d', [I, fTimes[K,I]]));
|
||
|
|
//
|
||
|
|
// //Footer
|
||
|
|
// S.Append('');
|
||
|
|
// S.Append('');
|
||
|
|
// end;
|
||
|
|
|
||
|
|
S.SaveToFile(aFilename);
|
||
|
|
S.Free;
|
||
|
|
end;
|
||
|
|
|
||
|
|
|
||
|
|
end.
|