241 lines
7.0 KiB
C++
241 lines
7.0 KiB
C++
/* ========================================================================
|
|
|
|
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Please see https://computerenhance.com for more information
|
|
|
|
======================================================================== */
|
|
|
|
/* ========================================================================
|
|
LISTING 109
|
|
======================================================================== */
|
|
|
|
enum test_mode : u32
|
|
{
|
|
TestMode_Uninitialized,
|
|
TestMode_Testing,
|
|
TestMode_Completed,
|
|
TestMode_Error,
|
|
};
|
|
|
|
enum repetition_value_type
|
|
{
|
|
RepValue_TestCount,
|
|
|
|
RepValue_CPUTimer,
|
|
RepValue_MemPageFaults,
|
|
RepValue_ByteCount,
|
|
|
|
RepValue_Count,
|
|
};
|
|
|
|
struct repetition_value
|
|
{
|
|
u64 E[RepValue_Count];
|
|
};
|
|
|
|
struct repetition_test_results
|
|
{
|
|
repetition_value Total;
|
|
repetition_value Min;
|
|
repetition_value Max;
|
|
};
|
|
|
|
struct repetition_tester
|
|
{
|
|
u64 TargetProcessedByteCount;
|
|
u64 CPUTimerFreq;
|
|
u64 TryForTime;
|
|
u64 TestsStartedAt;
|
|
|
|
test_mode Mode;
|
|
b32 PrintNewMinimums;
|
|
u32 OpenBlockCount;
|
|
u32 CloseBlockCount;
|
|
|
|
repetition_value AccumulatedOnThisTest;
|
|
repetition_test_results Results;
|
|
};
|
|
|
|
static f64 SecondsFromCPUTime(f64 CPUTime, u64 CPUTimerFreq)
|
|
{
|
|
f64 Result = 0.0;
|
|
if(CPUTimerFreq)
|
|
{
|
|
Result = (CPUTime / (f64)CPUTimerFreq);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
static void PrintValue(char const *Label, repetition_value Value, u64 CPUTimerFreq)
|
|
{
|
|
u64 TestCount = Value.E[RepValue_TestCount];
|
|
f64 Divisor = TestCount ? (f64)TestCount : 1;
|
|
|
|
f64 E[RepValue_Count];
|
|
for(u32 EIndex = 0; EIndex < ArrayCount(E); ++EIndex)
|
|
{
|
|
E[EIndex] = (f64)Value.E[EIndex] / Divisor;
|
|
}
|
|
|
|
printf("%s: %.0f", Label, E[RepValue_CPUTimer]);
|
|
if(CPUTimerFreq)
|
|
{
|
|
f64 Seconds = SecondsFromCPUTime(E[RepValue_CPUTimer], CPUTimerFreq);
|
|
printf(" (%fms)", 1000.0f*Seconds);
|
|
|
|
if(E[RepValue_ByteCount] > 0)
|
|
{
|
|
f64 Gigabyte = (1024.0f * 1024.0f * 1024.0f);
|
|
f64 Bandwidth = E[RepValue_ByteCount] / (Gigabyte * Seconds);
|
|
printf(" %fgb/s", Bandwidth);
|
|
}
|
|
}
|
|
|
|
if(E[RepValue_MemPageFaults] > 0)
|
|
{
|
|
printf(" PF: %0.4f (%0.4fk/fault)", E[RepValue_MemPageFaults], E[RepValue_ByteCount] / (E[RepValue_MemPageFaults] * 1024.0));
|
|
}
|
|
}
|
|
|
|
static void PrintResults(repetition_test_results Results, u64 CPUTimerFreq)
|
|
{
|
|
PrintValue("Min", Results.Min, CPUTimerFreq);
|
|
printf("\n");
|
|
PrintValue("Max", Results.Max, CPUTimerFreq);
|
|
printf("\n");
|
|
PrintValue("Avg", Results.Total, CPUTimerFreq);
|
|
printf("\n");
|
|
}
|
|
|
|
static void Error(repetition_tester *Tester, char const *Message)
|
|
{
|
|
Tester->Mode = TestMode_Error;
|
|
fprintf(stderr, "ERROR: %s\n", Message);
|
|
}
|
|
|
|
static void NewTestWave(repetition_tester *Tester, u64 TargetProcessedByteCount, u64 CPUTimerFreq, u32 SecondsToTry = 10)
|
|
{
|
|
if(Tester->Mode == TestMode_Uninitialized)
|
|
{
|
|
Tester->Mode = TestMode_Testing;
|
|
Tester->TargetProcessedByteCount = TargetProcessedByteCount;
|
|
Tester->CPUTimerFreq = CPUTimerFreq;
|
|
Tester->PrintNewMinimums = true;
|
|
Tester->Results.Min.E[RepValue_CPUTimer] = (u64)-1;
|
|
}
|
|
else if(Tester->Mode == TestMode_Completed)
|
|
{
|
|
Tester->Mode = TestMode_Testing;
|
|
|
|
if(Tester->TargetProcessedByteCount != TargetProcessedByteCount)
|
|
{
|
|
Error(Tester, "TargetProcessedByteCount changed");
|
|
}
|
|
|
|
if(Tester->CPUTimerFreq != CPUTimerFreq)
|
|
{
|
|
Error(Tester, "CPU frequency changed");
|
|
}
|
|
}
|
|
|
|
Tester->TryForTime = SecondsToTry*CPUTimerFreq;
|
|
Tester->TestsStartedAt = ReadCPUTimer();
|
|
}
|
|
|
|
static void BeginTime(repetition_tester *Tester)
|
|
{
|
|
++Tester->OpenBlockCount;
|
|
|
|
repetition_value *Accum = &Tester->AccumulatedOnThisTest;
|
|
Accum->E[RepValue_MemPageFaults] -= ReadOSPageFaultCount();
|
|
Accum->E[RepValue_CPUTimer] -= ReadCPUTimer();
|
|
}
|
|
|
|
static void EndTime(repetition_tester *Tester)
|
|
{
|
|
repetition_value *Accum = &Tester->AccumulatedOnThisTest;
|
|
Accum->E[RepValue_CPUTimer] += ReadCPUTimer();
|
|
Accum->E[RepValue_MemPageFaults] += ReadOSPageFaultCount();
|
|
|
|
++Tester->CloseBlockCount;
|
|
}
|
|
|
|
static void CountBytes(repetition_tester *Tester, u64 ByteCount)
|
|
{
|
|
repetition_value *Accum = &Tester->AccumulatedOnThisTest;
|
|
Accum->E[RepValue_ByteCount] += ByteCount;
|
|
}
|
|
|
|
static b32 IsTesting(repetition_tester *Tester)
|
|
{
|
|
if(Tester->Mode == TestMode_Testing)
|
|
{
|
|
repetition_value Accum = Tester->AccumulatedOnThisTest;
|
|
u64 CurrentTime = ReadCPUTimer();
|
|
|
|
if(Tester->OpenBlockCount) // NOTE(casey): We don't count tests that had no timing blocks - we assume they took some other path
|
|
{
|
|
if(Tester->OpenBlockCount != Tester->CloseBlockCount)
|
|
{
|
|
Error(Tester, "Unbalanced BeginTime/EndTime");
|
|
}
|
|
|
|
if(Accum.E[RepValue_ByteCount] != Tester->TargetProcessedByteCount)
|
|
{
|
|
Error(Tester, "Processed byte count mismatch");
|
|
}
|
|
|
|
if(Tester->Mode == TestMode_Testing)
|
|
{
|
|
repetition_test_results *Results = &Tester->Results;
|
|
|
|
Accum.E[RepValue_TestCount] = 1;
|
|
for(u32 EIndex = 0; EIndex < ArrayCount(Accum.E); ++EIndex)
|
|
{
|
|
Results->Total.E[EIndex] += Accum.E[EIndex];
|
|
}
|
|
|
|
if(Results->Max.E[RepValue_CPUTimer] < Accum.E[RepValue_CPUTimer])
|
|
{
|
|
Results->Max = Accum;
|
|
}
|
|
|
|
if(Results->Min.E[RepValue_CPUTimer] > Accum.E[RepValue_CPUTimer])
|
|
{
|
|
Results->Min = Accum;
|
|
|
|
// NOTE(casey): Whenever we get a new minimum time, we reset the clock to the full trial time
|
|
Tester->TestsStartedAt = CurrentTime;
|
|
|
|
if(Tester->PrintNewMinimums)
|
|
{
|
|
PrintValue("Min", Results->Min, Tester->CPUTimerFreq);
|
|
printf(" \r");
|
|
}
|
|
}
|
|
|
|
Tester->OpenBlockCount = 0;
|
|
Tester->CloseBlockCount = 0;
|
|
Tester->AccumulatedOnThisTest = {};
|
|
}
|
|
}
|
|
|
|
if((CurrentTime - Tester->TestsStartedAt) > Tester->TryForTime)
|
|
{
|
|
Tester->Mode = TestMode_Completed;
|
|
|
|
printf(" \r");
|
|
PrintResults(Tester->Results, Tester->CPUTimerFreq);
|
|
}
|
|
}
|
|
|
|
b32 Result = (Tester->Mode == TestMode_Testing);
|
|
return Result;
|
|
}
|