[+] AuHWInfo::ICoreLoadSampler

[+] AuHWInfo::CpuCoreTime
[+] AuHWInfo::GetPerCoreCPUTime
This commit is contained in:
Reece Wilson 2023-12-31 23:13:26 +00:00
parent 7b59cab36e
commit c64cb0c0e2
7 changed files with 662 additions and 1 deletions

View File

@ -0,0 +1,23 @@
/***
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: CoreLoad.hpp
Date: 2023-12-30
Author: Reece
***/
#pragma once
namespace Aurora::HWInfo
{
struct CpuCoreTime
{
AuUInt64 uUptime {}; // uUserTime + uKernelTime + ...
AuUInt64 uAllPowerUpTime {};
AuUInt64 uIdleTime {}; // no activity
AuUInt64 uKernelTime {};
AuUInt64 uInterruptTime {};
AuUInt64 uUserTime {};
};
AUKN_SYM bool GetPerCoreCPUTime(AuList<CpuCoreTime> &times);
}

View File

@ -0,0 +1,33 @@
/***
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: CoreLoadSampler.hpp
Date: 2023-12-30
Author: Reece
***/
#pragma once
namespace Aurora::HWInfo
{
struct ICoreLoadSampler
{
// 0-100%
virtual double GetCoreLoad(AuUInt8 uCore) = 0;
// GetCoreLoad (cont, with consistent results)
virtual double GetCoreLoadCached(AuUInt8 uCore) = 0;
// 0-100%
virtual double GetTotalLoad() = 0;
virtual double GetTotalLoadCached() = 0;
virtual double *GetCoreLoadsCached(AuUInt32 *pOut) = 0;
};
AUKN_SHARED_API(CoreLoadSampler, ICoreLoadSampler,
AuUInt32 uMinSamplePeriodMS = AuSToMS<AuUInt32>(1),
bool bCountKernelUsage = true);
AUKN_SYM ICoreLoadSampler *GetDefaultCoreLoadSampler();
}

View File

@ -9,4 +9,6 @@
#include "CpuInfo.hpp"
#include "RamInfo.hpp"
#include "CpuLoadSampler.hpp"
#include "CpuLoadSampler.hpp"
#include "CoreLoad.hpp"
#include "CoreLoadSampler.hpp"

View File

@ -0,0 +1,232 @@
/***
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuCoreLoadSampler.cpp
Date: 2023-12-30
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuCoreLoadSampler.hpp"
namespace Aurora::HWInfo
{
static CoreLoadSamplerImpl gDefaultCoreUsageSampler(AuSToMS<AuUInt32>(1), true);
CoreLoadSamplerImpl::CoreLoadSamplerImpl(AuUInt32 uMinSamplePeriodMS,
bool bCountKernelUsage) :
uMinSamplePeriod(uMinSamplePeriodMS),
bCountKernelUsage(bCountKernelUsage)
{
}
CoreLoadSamplerImpl::~CoreLoadSamplerImpl()
{
}
double CoreLoadSamplerImpl::GetCoreLoad(AuUInt8 uCore)
{
return this->state.GetLoad(this->uMinSamplePeriod,
uCore,
this->bCountKernelUsage);
}
double CoreLoadSamplerImpl::GetCoreLoadCached(AuUInt8 uCore)
{
if (uCore >= AuArraySize(this->state.dCurrentLoad))
{
return 0.0f;
}
return this->state.dCurrentLoad[uCore];
}
double CoreLoadSamplerImpl::GetTotalLoad()
{
(void)this->GetCoreLoad(0);
return this->GetTotalLoadCached();
}
double CoreLoadSamplerImpl::GetTotalLoadCached()
{
return this->state.dCurrentLoadAll;
}
double CoreLoadSamplerState::GetLoad(AuUInt32 uMinSamplePeriod,
AuUInt8 uThread,
bool bCountKernelUsage)
{
AuUInt64 uCurrentTimes[256] {};
AuUInt64 uNow {};
double dDeltaCore[256];
double dAverage {}, dDeltaSteady {};
AuList<CpuCoreTime> times;
double dUsage[256] { 0.0f };
double dMinSamplePeriod = double(AuMSToNS<AuUInt64>(uMinSamplePeriod));
if (!GetPerCoreCPUTime(times))
{
return 0;
}
uNow = AuTime::SteadyClockNS();
dDeltaSteady = uNow - this->uPrevSteadyTime;
if (!this->uCount)
{
auto uThreads = GetCPUInfo().uThreads;
this->uCount = AuMin(uThreads, AuArraySize(uCurrentTimes));
this->uCount = AuMin(this->uCount, times.size());
}
for (AU_ITERATE_N(i, this->uCount))
{
if (times.size() > i)
{
if (bCountKernelUsage)
{
uCurrentTimes[i] = times[i].uUptime;
}
else
{
uCurrentTimes[i] = times[i].uUserTime;
}
}
dDeltaCore[i] = uCurrentTimes[i] - this->uPrevTimes[i];
}
if (!bool(this->uPrevSteadyTime))
{
this->uPrevSteadyTime = uNow;
AuMemcpy(this->uPrevTimes, uCurrentTimes, sizeof(uCurrentTimes));
return 0;
}
if (!uMinSamplePeriod ||
(dDeltaSteady >= dMinSamplePeriod))
{
AuMemcpy(this->uPrevTimes, uCurrentTimes, sizeof(uCurrentTimes));
this->uPrevSteadyTime = uNow;
this->bSet = true;
for (AU_ITERATE_N(i, this->uCount))
{
dUsage[i] = dDeltaCore[i] / dDeltaSteady;
if (uMinSamplePeriod)
{
if (dUsage[i] > AuNumericLimits<double>::epsilon() &&
this->dPrevLoad[i] > AuNumericLimits<double>::epsilon())
{
dUsage[i] = AuMin(dUsage[i], dUsage[i] + this->dPrevLoad[i] / 2.0);
}
else
{
dUsage[i] = AuMax(this->dPrevLoad[i] / 2.0, 0.0001);
}
}
else if (!dUsage)
{
dUsage[i] = AuMax(this->dPrevLoad[i] / 2.0, 0.0001);
}
this->dPrevLoad[i] = dUsage[i];
}
}
else
{
for (AU_ITERATE_N(i, this->uCount))
{
if (dDeltaCore[i])
{
dUsage[i] = dDeltaCore[i] / dDeltaSteady;
if (this->bSet)
{
#if 0
dUsage[i] *= dDeltaSteady / double(dMinSamplePeriod);
if (this->dPrevLoad[i]) dUsage[i] = dUsage[i] + this->dPrevLoad[i] / 2.0;
#else
if (this->dPrevLoad[i])
{
auto dFrameDelta = dDeltaSteady / dMinSamplePeriod;
auto dFrameDeltaInverse = 1.0 - dFrameDelta;
dUsage[i] *= dFrameDelta;
dUsage[i] += this->dPrevLoad[i] * dFrameDeltaInverse;
}
else
{
dUsage[i] *= dDeltaSteady / dMinSamplePeriod;
}
#endif
}
}
else
{
dUsage[i] = this->dPrevLoad[i];
}
}
}
for (AU_ITERATE_N(i, this->uCount))
{
dUsage[i] = dUsage[i] * 100.0;
if (dUsage[i] > 100.0)
{
dUsage[i] = 100.0;
}
else if (dUsage[i] < 0.0)
{
dUsage[i] = 0.0;
}
dAverage += dUsage[i];
}
dAverage /= this->uCount;
this->dCurrentLoadAll = dAverage;
AuMemcpy(this->dCurrentLoad, dUsage, sizeof(dUsage));
if (uThread >= AuArraySize(dUsage))
{
return 0;
}
else
{
return dUsage[uThread];
}
}
double *CoreLoadSamplerImpl::GetCoreLoadsCached(AuUInt32 *pOut)
{
if (!this->state.uCount ||
!pOut)
{
return nullptr;
}
*pOut = this->state.uCount;
return this->state.dCurrentLoad;
}
AUKN_SYM ICoreLoadSampler *CoreLoadSamplerNew(AuUInt32 uMinSamplePeriodMS,
bool bCountKernelUsage)
{
return _new CoreLoadSamplerImpl(uMinSamplePeriodMS, bCountKernelUsage);
}
AUKN_SYM void CoreLoadSamplerRelease(ICoreLoadSampler *pSampler)
{
AuSafeDelete<CoreLoadSamplerImpl *>(pSampler);
}
AUKN_SYM ICoreLoadSampler *GetDefaultCoreLoadSampler()
{
return &gDefaultCoreUsageSampler;
}
}

View File

@ -0,0 +1,44 @@
/***
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuCpuLoadSampler.hpp
Date: 2023-10-28
Author: Reece
***/
#pragma once
namespace Aurora::HWInfo
{
struct CoreLoadSamplerState
{
double GetLoad(AuUInt32 uMinSamplePeriod,
AuUInt8 uThread,
bool bCountKernelUsage);
AuUInt64 uPrevSteadyTime {};
AuUInt64 uPrevTimes[256] {};
double dPrevLoad[256] {};
double dCurrentLoad[256] {};
double dCurrentLoadAll {};
AuUInt64 uCount {};
bool bSet {};
};
struct CoreLoadSamplerImpl : ICoreLoadSampler
{
CoreLoadSamplerImpl(AuUInt32 uMinSamplePeriodMS,
bool bCountKernelUsage);
~CoreLoadSamplerImpl();
double GetCoreLoad(AuUInt8 uCore) override;
double GetTotalLoad() override;
double GetTotalLoadCached() override;
double GetCoreLoadCached(AuUInt8 uCore) override;
double *GetCoreLoadsCached(AuUInt32 *pOut) override;
AuUInt32 uMinSamplePeriod {};
bool bCountKernelUsage {};
CoreLoadSamplerState state;
};
}

View File

@ -0,0 +1,314 @@
/***
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuCpuTimes.cpp
Date: 2023-12-30
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuCpuTimes.hpp"
#if defined(__FreeBSD__)
#include <errno.h>
#include <sys/sysctl.h>
#include <sys/resource.h>
#elif defined(AURORA_IS_XNU_DERIVED)
#include <sys/dkstat.h>
#elif defined(AURORA_IS_LINUX_DERIVED)
#if !defined(USER_HZ)
#define USER_HZ 100
#endif
#endif
namespace Aurora::HWInfo
{
static AuUInt64 ConvertTicks(AuUInt64 uTicks)
{
#if defined(AURORA_IS_MODERNNT_DERIVED)
return uTicks * 100ull;
#else
static const auto kFreq =
#if defined(AURORA_IS_LINUX_DERIVED)
USER_HZ
#else
CLOCKS_PER_SEC
#endif
;
if (kFreq == 10000000)
{
return uTicks * 100ull;
}
else if (kFreq == 1000000)
{
return uTicks * 1000ull;
}
else if (kFreq == 100000)
{
return uTicks * 10000ull;
}
else if (kFreq == 10000)
{
return uTicks * 100000ull;
}
else if (kFreq == 1000)
{
return uTicks * 1000000ull;
}
else if (kFreq == 100)
{
return uTicks * 10000000ull;
}
else if (kFreq == 100000000)
{
return uTicks * 10ull;
}
else if (kFreq == 1000000000ull)
{
return uTicks;
}
else
{
const long long uWhole = (uTicks / kFreq) * 1'000'000'000ull;
const long long uPart = (uTicks % kFreq) * 1'000'000'000ull / kFreq;
return uWhole + uPart;
}
#endif
}
#if defined(__FreeBSD__)
static int CpuSysctl(const char *pName, void *pBuffer, size_t *pLength)
{
int error = sysctlbyname(pName, pBuffer, pLength, NULL, 0);
if (error != 0 && errno != ENOMEM)
{
SysPushErrorGeneric(errno);
return -1;
}
return (error);
}
static void PlatformGetCoreTimes(AuList<CpuCoreTime> &times)
{
auto uThreads = AuHwInfo::GetCPUInfo().uThreads;
AuList<long> cpuStates;
cpuStates.resize((uThreads + 1) * CPUSTATES);
size_t len { cpuStates.size() * sizeof(long) };
if (CpuSysctl("kern.cp_times", cpuStates.data(), &len) < 0)
{
return;
}
times.resize(uThreads);
for (AU_ITERATE_N(uCore, uThreads))
{
auto &coreTimes = times[uCore];
coreTimes.uKernelTime = cpuStates[(CPUSTATES * uCore) + CP_SYS];
#if defined(CP_INTR)
coreTimes.uInterruptTime = cpuStates[(CPUSTATES * uCore) + CP_INTR];
#endif
coreTimes.uUserTime = cpuStates[(CPUSTATES * uCore) + CP_USER];
coreTimes.uIdleTime = cpuStates[(CPUSTATES * uCore) + CP_IDLE];
coreTimes.uIdleTime = ConvertTicks(coreTimes.uIdleTime);
coreTimes.uInterruptTime = ConvertTicks(coreTimes.uInterruptTime);
coreTimes.uKernelTime = ConvertTicks(coreTimes.uKernelTime);
coreTimes.uUserTime = ConvertTicks(coreTimes.uUserTime);
coreTimes.uUptime = coreTimes.uKernelTime + coreTimes.uUserTime + coreTimes.uInterruptTime;
coreTimes.uAllPowerUpTime = coreTimes.uIdleTime +
coreTimes.uUptime;
}
}
#elif defined(AURORA_IS_LINUX_DERIVED)
static bool PlatformGetCoreTimes(AuList<CpuCoreTime> &times)
{
bool bStatus { true };
AuString statFile {};
if (!AuFS::ReadString("/proc/stat", statFile))
{
return false;
}
AuParse::SplitNewlines(statFile, [&](const AuString &line)
{
CpuCoreTime coreTimes;
if (!AuStartsWith(line, "cpu"))
{
return;
}
auto line2 = line;
auto intStrs = AuSplitString(line2, " ");
intStrs.erase(intStrs.begin());
if (intStrs.size() > 0)
{
if (auto result = AuParse::ParseUInt(intStrs[0]))
{
coreTimes.uUserTime = result.value();
}
}
if (intStrs.size() > 1)
{
if (auto result = AuParse::ParseUInt(intStrs[1]))
{
coreTimes.uUserTime += result.value();
}
}
if (intStrs.size() > 2)
{
if (auto result = AuParse::ParseUInt(intStrs[2]))
{
coreTimes.uKernelTime = result.value();
}
}
if (intStrs.size() > 3)
{
if (auto result = AuParse::ParseUInt(intStrs[3]))
{
coreTimes.uIdleTime = result.value();
}
}
if (intStrs.size() > 4)
{
if (auto result = AuParse::ParseUInt(intStrs[4]))
{
coreTimes.uKernelTime += result.value();
}
}
if (intStrs.size() > 5)
{
if (auto result = AuParse::ParseUInt(intStrs[5]))
{
coreTimes.uInterruptTime += result.value();
}
}
if (intStrs.size() > 6)
{
if (auto result = AuParse::ParseUInt(intStrs[6]))
{
coreTimes.uInterruptTime += result.value();
}
}
for (AU_ITERATE_N_TO_X(i, 7, intStrs.size()))
{
if (auto result = AuParse::ParseUInt(intStrs[i]))
{
coreTimes.uKernelTime += result.value();
}
}
coreTimes.uIdleTime = ConvertTicks(coreTimes.uIdleTime);
coreTimes.uInterruptTime = ConvertTicks(coreTimes.uInterruptTime);
coreTimes.uKernelTime = ConvertTicks(coreTimes.uKernelTime);
coreTimes.uUserTime = ConvertTicks(coreTimes.uUserTime);
coreTimes.uUptime = coreTimes.uKernelTime +
coreTimes.uUserTime +
coreTimes.uInterruptTime;
coreTimes.uAllPowerUpTime = coreTimes.uIdleTime +
coreTimes.uUptime;
times.push_back(coreTimes);
});
return bStatus;
}
#elif defined(AURORA_IS_MODERNNT_DERIVED)
static bool PlatformGetCoreTimes(AuList<CpuCoreTime> &times)
{
#if defined(_AU_MASSIVE_CPUID)
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION processorInfo[256];
#else
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION processorInfo[128];
#endif
ULONG uOutLen {};
if (FAILED(NtQuerySystemInformation(SystemProcessorPerformanceInformation,
&processorInfo,
sizeof(processorInfo),
&uOutLen)))
{
return false;
}
auto uCount = uOutLen / sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
if (!AuTryResize(times, uCount))
{
return false;
}
for (AU_ITERATE_N(uCore, uCount))
{
auto &coreTimes = times[uCore];
coreTimes.uIdleTime = ConvertTicks(processorInfo[uCore].IdleTime.QuadPart);
coreTimes.uInterruptTime = 0;
coreTimes.uKernelTime = ConvertTicks(processorInfo[uCore].KernelTime.QuadPart);
coreTimes.uUserTime = ConvertTicks(processorInfo[uCore].UserTime.QuadPart);
coreTimes.uKernelTime -= coreTimes.uIdleTime;
coreTimes.uUptime = coreTimes.uKernelTime +
coreTimes.uUserTime +
coreTimes.uInterruptTime;
coreTimes.uAllPowerUpTime = coreTimes.uIdleTime +
coreTimes.uUptime;
}
return true;
}
#elif defined(AURORA_IS_POSIX_DERIVED)
static bool PlatformGetCoreTimes(AuList<CpuCoreTime> &times)
{
return false;
}
#else
static bool PlatformGetCoreTimes(AuList<CpuCoreTime> &times)
{
return false;
}
#endif
AUKN_SYM bool GetPerCoreCPUTime(AuList<CpuCoreTime> &times)
{
return PlatformGetCoreTimes(times);
}
}

View File

@ -0,0 +1,13 @@
/***
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuCpuTimes.hpp
Date: 2023-12-30
Author: Reece
***/
#pragma once
namespace Aurora::HWInfo
{
}