Files
source/Ja2/profiler.cpp
T
00fb898600 Delete builddefines.h
Nothing was left in it but the include of profiler.h, and 132 translation units
were including it for that alone. Fifteen files were leaning on profiler.h to
drag in <set>, <vector> and <ostream> for them; those now include what they use.

This is the commit that moves line numbers. Removing an include line shifts
__LINE__ by one for everything below it, and __LINE__ is an immediate operand in
every Assert() and DebugMsg() call, so the four game executables differ from
their predecessors by roughly a thousand 32-bit constants each. Every one of
those is accounted for: each is a single immediate that moved by -1 where the
builddefines.h include went away, or +1 where a <set>/<vector> include was
added. Nothing else in .text, .rdata or .data moves, no object file's section
sizes change, and symbolize_crash and Ja2Export stay bit-identical.

The one non-immediate difference is that the 24 Editor translation units of the
non-editor apps stop emitting __Avx2WmemEnabledWeakValue, a 4-byte weak COMDAT
they only ever instantiated through profiler.h's <vector>. It is a UCRT weak
default that other translation units still provide.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 01:07:26 -03:00

442 lines
11 KiB
C++

#ifdef PROFILER_ENABLED
#include <sstream>
#ifdef PERIODIC_PROFILING
bool gRecordingProfile = false;
#endif
PerfManager* PerfManager::_instance(NULL);
PerfDatum::PerfDatum( const char* const fileName,
const char* const functionName,
const int lineNumber,
__int64 cycles,
__int64 calls)
:
_fileName(fileName),
_functionName(functionName),
_lineNumber(lineNumber),
_cycles(cycles),
_calls(calls)
{
}
PerfManager::PerfManager(void)
:
_callTime(0),
_totalTime(0)
{
//Set up a root function so that the stack is never empty:
//(this will be the only entry with _lineNumber equal to 0)
_perfLog.insert(PerfDatum(__FILE__, "Root",0));
_perfStack.push_back(&*(_perfLog.begin()));
_lastTime = getCPUCount();
calibrate();
}
void PerfManager::calibrate()
{
}
PerfManager::~PerfManager(void)
{
}
PerfManager* PerfManager::instance()
{
if(!_instance) //Double-check locking (first check)
{ _instance = new PerfManager();
}
return _instance;
}
void PerfManager::enterFunction(const char* const fileName,
const char* const functionName,
const int lineNumber)
{
//Mark the time when we end processing the previous function
__int64 endTime = getCPUCount();
//assuming we haven't corrupted the stack and reduced it to zero elements
if(_perfStack.size())
{ PERF_STACK_T::reverse_iterator lastMarker = _perfStack.rbegin();
//Update the total time spent processing the previous function
__int64 runTime = (endTime - _lastTime) - _callTime;
(**lastMarker)._cycles += runTime;
_totalTime += runTime;
}
//If we fail to insert the performance marker into our log, it means it is
//a duplicate. We can ignore this "error" as using the existing copy is
//the correct behavior.
std::pair<PERF_LOG_T::iterator, bool> result =
_perfLog.insert(PerfDatum(fileName, functionName, lineNumber));
//Update the call count on the new function:
PerfDatum* marker = &*(result.first);
marker->_calls++;
//Add performance marker to the stack as well as the log:
_perfStack.push_back(marker);
//Now that we are clear of any complex operations, we can set the call
//time of the new marker more accurately:
_lastTime = getCPUCount();
}
void PerfManager::exitFunction()
{
//Mark the time when we exited the function
__int64 endTime = getCPUCount();
//Update the function in our log with its new runtime
PERF_STACK_T::reverse_iterator lastMarker = _perfStack.rbegin();
__int64 runTime = endTime - _lastTime;
(**lastMarker)._cycles += runTime;
_totalTime += runTime;
_perfStack.pop_back();
//Lastly, update the function we've fallen back to with the new current
//run time.
_lastTime = getCPUCount();
}
__int64 PerfManager::getCPUCount () const
{
_asm rdtsc
}
bool PerfSort::operator ()(const PerfDatum& lhs, const PerfDatum&rhs) const
{
if(rhs._lineNumber == lhs._lineNumber)
{ return 0 < strcmp(rhs._fileName, lhs._fileName);
}
else
{ return rhs._lineNumber > lhs._lineNumber;
}
}
bool PerfSort2::operator ()(const PerfDatum& lhs, const PerfDatum&rhs) const
{
return lhs._cycles > rhs._cycles;
}
int PerfManager::getPercision(const double value) const
{
int retVal = 1;
if(10.0 <= value)
{ retVal = 3;
}
else if (1.0 <= value)
{ retVal = 2;
}
return retVal;
}
void PerfManager::log(std::ostream &os)
{
//Create a copy of the log that sorts by cycles used
std::set<PerfDatum, PerfSort2> logCopy;
for(PERF_LOG_T::iterator i = _perfLog.begin(); i != _perfLog.end(); i++)
{
logCopy.insert(*i);
}
int cyclesWidth = 1;
//I want the formatting to be nice!
for(std::set<PerfDatum, PerfSort2>::iterator i = logCopy.begin();
i != logCopy.end();
i++)
{
if(i->_lineNumber) {
std::stringstream cyclesString;
cyclesString << i->_cycles;
std::string tempString;
cyclesString >> tempString;
cyclesWidth = tempString.size();
break;
}
}
//Stream the sorted log to the supplied ostream
for(std::set<PerfDatum, PerfSort2>::iterator i = logCopy.begin();
i != logCopy.end();
++i)
{
if(i->_lineNumber)
{ //Truncate path from file name if the compiler included it.
const char* j = i->_fileName + strlen(i->_fileName);
while('\\' != *j && '/' != *j && j >= i->_fileName){j--;}
if('\\' == *j || '/' == *j){j++;}
std::stringstream cyclesString;
std::string tempString;
cyclesString << i->_cycles;
cyclesString >> tempString;
os.width(cyclesWidth);
os << tempString.c_str() << ", ";
__int64 cyclesPerCall = i->_cycles / i->_calls;
cyclesString.clear();
tempString.clear();
cyclesString << cyclesPerCall;
cyclesString >> tempString;
os.width(cyclesWidth);
os << tempString.c_str() << "] cycles (";
double percent = double(i->_cycles) / double(_totalTime) * 100;
os.precision(getPercision(percent));
os.width(4);
os << percent << "%) used in [";
os.width(8);
os << i->_calls << "] calls by " << j << "::" << i->_functionName\
<< " at line " << i->_lineNumber << std::endl;
}
}
}
PerfMarker::PerfMarker( const char* const fileName,
const char* const functionName,
const int lineNumber)
:
#ifdef PERIODIC_PROFILING
_onStack(gRecordingProfile)
#else
_onStack(true)
#endif
{
#ifdef PERIODIC_PROFILING
if (gRecordingProfile == true) {
#endif
PerfManager::instance()->enterFunction(fileName,functionName,lineNumber);
#ifdef PERIODIC_PROFILING
}
#endif
}
PerfMarker::~PerfMarker()
{
endMark();
}
void PerfMarker::endMark()
{
if(_onStack)
{ PerfManager::instance()->exitFunction();
_onStack = false;
}
}
#include "profiler.h"
#include <sstream>
#ifdef PERIODIC_PROFILING
bool gRecordingProfile = false;
#endif
PerfManager* PerfManager::_instance(NULL);
PerfDatum::PerfDatum( const char* const fileName,
const char* const functionName,
const int lineNumber,
__int64 cycles,
__int64 calls)
:
_fileName(fileName),
_functionName(functionName),
_lineNumber(lineNumber),
_cycles(cycles),
_calls(calls)
{
}
PerfManager::PerfManager(void)
:
_callTime(0),
_totalTime(0)
{
//Set up a root function so that the stack is never empty:
//(this will be the only entry with _lineNumber equal to 0)
_perfLog.insert(PerfDatum(__FILE__, "Root",0));
_perfStack.push_back(&*(_perfLog.begin()));
_lastTime = getCPUCount();
calibrate();
}
void PerfManager::calibrate()
{
}
PerfManager::~PerfManager(void)
{
}
PerfManager* PerfManager::instance()
{
if(!_instance) //Double-check locking (first check)
{ _instance = new PerfManager();
}
return _instance;
}
void PerfManager::enterFunction(const char* const fileName,
const char* const functionName,
const int lineNumber)
{
//Mark the time when we end processing the previous function
__int64 endTime = getCPUCount();
//assuming we haven't corrupted the stack and reduced it to zero elements
if(_perfStack.size())
{ PERF_STACK_T::reverse_iterator lastMarker = _perfStack.rbegin();
//Update the total time spent processing the previous function
__int64 runTime = (endTime - _lastTime) - _callTime;
(**lastMarker)._cycles += runTime;
_totalTime += runTime;
}
//If we fail to insert the performance marker into our log, it means it is
//a duplicate. We can ignore this "error" as using the existing copy is
//the correct behavior.
std::pair<PERF_LOG_T::iterator, bool> result =
_perfLog.insert(PerfDatum(fileName, functionName, lineNumber));
//Update the call count on the new function:
PerfDatum* marker = &*(result.first);
marker->_calls++;
//Add performance marker to the stack as well as the log:
_perfStack.push_back(marker);
//Now that we are clear of any complex operations, we can set the call
//time of the new marker more accurately:
_lastTime = getCPUCount();
}
void PerfManager::exitFunction()
{
//Mark the time when we exited the function
__int64 endTime = getCPUCount();
//Update the function in our log with its new runtime
PERF_STACK_T::reverse_iterator lastMarker = _perfStack.rbegin();
__int64 runTime = endTime - _lastTime;
(**lastMarker)._cycles += runTime;
_totalTime += runTime;
_perfStack.pop_back();
//Lastly, update the function we've fallen back to with the new current
//run time.
_lastTime = getCPUCount();
}
__int64 PerfManager::getCPUCount () const
{
_asm rdtsc
}
bool PerfSort::operator ()(const PerfDatum& lhs, const PerfDatum&rhs) const
{
if(rhs._lineNumber == lhs._lineNumber)
{ return 0 < strcmp(rhs._fileName, lhs._fileName);
}
else
{ return rhs._lineNumber > lhs._lineNumber;
}
}
bool PerfSort2::operator ()(const PerfDatum& lhs, const PerfDatum&rhs) const
{
return lhs._cycles > rhs._cycles;
}
int PerfManager::getPercision(const double value) const
{
int retVal = 1;
if(10.0 <= value)
{ retVal = 3;
}
else if (1.0 <= value)
{ retVal = 2;
}
return retVal;
}
void PerfManager::log(std::ostream &os)
{
//Create a copy of the log that sorts by cycles used
std::set<PerfDatum, PerfSort2> logCopy;
for(PERF_LOG_T::iterator i = _perfLog.begin(); i != _perfLog.end(); i++)
{
logCopy.insert(*i);
}
int cyclesWidth = 1;
//I want the formatting to be nice!
for(std::set<PerfDatum, PerfSort2>::iterator i = logCopy.begin();
i != logCopy.end();
i++)
{
if(i->_lineNumber) {
std::stringstream cyclesString;
cyclesString << i->_cycles;
std::string tempString;
cyclesString >> tempString;
cyclesWidth = tempString.size();
break;
}
}
//Stream the sorted log to the supplied ostream
for(std::set<PerfDatum, PerfSort2>::iterator i = logCopy.begin();
i != logCopy.end();
++i)
{
if(i->_lineNumber)
{ //Truncate path from file name if the compiler included it.
const char* j = i->_fileName + strlen(i->_fileName);
while('\\' != *j && '/' != *j && j >= i->_fileName){j--;}
if('\\' == *j || '/' == *j){j++;}
std::stringstream cyclesString;
std::string tempString;
cyclesString << i->_cycles;
cyclesString >> tempString;
os.width(cyclesWidth);
os << tempString.c_str() << ", ";
__int64 cyclesPerCall = i->_cycles / i->_calls;
cyclesString.clear();
tempString.clear();
cyclesString << cyclesPerCall;
cyclesString >> tempString;
os.width(cyclesWidth);
os << tempString.c_str() << "] cycles (";
double percent = double(i->_cycles) / double(_totalTime) * 100;
os.precision(getPercision(percent));
os.width(4);
os << percent << "%) used in [";
os.width(8);
os << i->_calls << "] calls by " << j << "::" << i->_functionName\
<< " at line " << i->_lineNumber << std::endl;
}
}
}
PerfMarker::PerfMarker( const char* const fileName,
const char* const functionName,
const int lineNumber)
:
#ifdef PERIODIC_PROFILING
_onStack(gRecordingProfile)
#else
_onStack(true)
#endif
{
#ifdef PERIODIC_PROFILING
if (gRecordingProfile == true) {
#endif
PerfManager::instance()->enterFunction(fileName,functionName,lineNumber);
#ifdef PERIODIC_PROFILING
}
#endif
}
PerfMarker::~PerfMarker()
{
endMark();
}
void PerfMarker::endMark()
{
if(_onStack)
{ PerfManager::instance()->exitFunction();
_onStack = false;
}
}
#endif