PerformanceAwareProgramming/part3/listing_0108_platform_metrics.cpp

146 lines
3.6 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 108
======================================================================== */
#if _WIN32
#include <intrin.h>
#include <windows.h>
#include <psapi.h>
typedef struct os_metrics
{
b32 Initialized;
HANDLE ProcessHandle;
} os_metrics;
static os_metrics GlobalMetrics;
static u64 GetOSTimerFreq(void)
{
LARGE_INTEGER Freq;
QueryPerformanceFrequency(&Freq);
return Freq.QuadPart;
}
static u64 ReadOSTimer(void)
{
LARGE_INTEGER Value;
QueryPerformanceCounter(&Value);
return Value.QuadPart;
}
static u64 ReadOSPageFaultCount(void)
{
PROCESS_MEMORY_COUNTERS_EX MemoryCounters = {};
MemoryCounters.cb = sizeof(MemoryCounters);
GetProcessMemoryInfo(GlobalMetrics.ProcessHandle, (PROCESS_MEMORY_COUNTERS *)&MemoryCounters, sizeof(MemoryCounters));
u64 Result = MemoryCounters.PageFaultCount;
return Result;
}
static void InitializeOSMetrics(void)
{
if(!GlobalMetrics.Initialized)
{
GlobalMetrics.Initialized = true;
GlobalMetrics.ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, GetCurrentProcessId());
}
}
#else
#include <x86intrin.h>
#include <sys/time.h>
static u64 GetOSTimerFreq(void)
{
return 1000000;
}
static u64 ReadOSTimer(void)
{
// NOTE(casey): The "struct" keyword is not necessary here when compiling in C++,
// but just in case anyone is using this file from C, I include it.
struct timeval Value;
gettimeofday(&Value, 0);
u64 Result = GetOSTimerFreq()*(u64)Value.tv_sec + (u64)Value.tv_usec;
return Result;
}
static u64 ReadOSPageFaultCount(void)
{
// NOTE(casey): The course materials are not tested on MacOS/Linux.
// This code was contributed to the public github. It may or may not work
// for your system.
struct rusage Usage = {};
getrusage(RUSAGE_SELF, &Usage);
// ru_minflt the number of page faults serviced without any I/O activity.
// ru_majflt the number of page faults serviced that required I/O activity.
u64 Result = Usage.ru_minflt + Usage.ru_majflt;
return Result;
}
static void InitializeOSMetrics(void)
{
}
#endif
/* NOTE(casey): This does not need to be "inline", it could just be "static"
because compilers will inline it anyway. But compilers will warn about
static functions that aren't used. So "inline" is just the simplest way
to tell them to stop complaining about that. */
inline u64 ReadCPUTimer(void)
{
// NOTE(casey): If you were on ARM, you would need to replace __rdtsc
// with one of their performance counter read instructions, depending
// on which ones are available on your platform.
return __rdtsc();
}
static u64 EstimateCPUTimerFreq(void)
{
u64 MillisecondsToWait = 100;
u64 OSFreq = GetOSTimerFreq();
u64 CPUStart = ReadCPUTimer();
u64 OSStart = ReadOSTimer();
u64 OSEnd = 0;
u64 OSElapsed = 0;
u64 OSWaitTime = OSFreq * MillisecondsToWait / 1000;
while(OSElapsed < OSWaitTime)
{
OSEnd = ReadOSTimer();
OSElapsed = OSEnd - OSStart;
}
u64 CPUEnd = ReadCPUTimer();
u64 CPUElapsed = CPUEnd - CPUStart;
u64 CPUFreq = 0;
if(OSElapsed)
{
CPUFreq = OSFreq * CPUElapsed / OSElapsed;
}
return CPUFreq;
}