/*** Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: CpuLoadSampler.cpp Date: 2023-10-28 Author: Reece ***/ #include #include "AuCpuLoadSampler.hpp" namespace Aurora::HWInfo { static thread_local CpuLoadSamplerImpl tlsThreadLocalUsageSamplers[2] { CpuLoadSamplerImpl(AuSToMS(1), true, false), CpuLoadSamplerImpl(AuSToMS(1), true, true) }; static CpuLoadSamplerImpl gProcessUsageSampler(AuSToMS(1), false, true); CpuLoadSamplerImpl::CpuLoadSamplerImpl(AuUInt32 uMinSamplePeriodMS, bool bThreadMode, bool bCountKernelUsage) : uMinSamplePeriod(uMinSamplePeriodMS), bThreadMode(bThreadMode), bCountKernelUsage(bCountKernelUsage) { } CpuLoadSamplerImpl::~CpuLoadSamplerImpl() { } double CpuLoadSamplerImpl::GetLoad() { if (!this->bThreadMode) { return this->processState.GetLoad(this->uMinSamplePeriod, false, this->bCountKernelUsage); } else { return this->threadState->GetLoad(this->uMinSamplePeriod, true, this->bCountKernelUsage); } } double CpuLoadSamplerState::GetLoad(AuUInt32 uMinSamplePeriod, bool bThread, bool bCountKernelUsage) { AuUInt64 now[2] = { AuTime::SteadyClockNS(), bThread ? (bCountKernelUsage ? AuTime::ThreadClockNS() : AuTime::ThreadUserClockNS()) : (bCountKernelUsage ? AuTime::ProcessClockNS() : AuTime::ProcessUserClockNS()) }; double dDeltaSteady = now[0] - this->uPrevTimes[0]; double dDeltaProcess = now[1] - this->uPrevTimes[1]; double dUsage = 0; double dMinSamplePeriod = double(AuMSToNS(uMinSamplePeriod)); double dDeltaSteady2; double dThreads = 1.0; if (bThread) { dThreads = GetCPUInfo().uThreads; dDeltaSteady2 = dDeltaSteady * dThreads; } else { dDeltaSteady2 = dDeltaSteady; } if (!bool(this->uPrevTimes[0])) { this->uPrevTimes[0] = now[0]; this->uPrevTimes[1] = now[1]; return 0; } if (!uMinSamplePeriod || (dDeltaSteady >= dMinSamplePeriod)) { dUsage = dDeltaProcess / dDeltaSteady2; this->uPrevTimes[1] = now[1]; this->uPrevTimes[0] = now[0]; if (uMinSamplePeriod) { if (dUsage > AuNumericLimits::epsilon() && this->dPrevLoad > AuNumericLimits::epsilon()) { dUsage = AuMin(dUsage, dUsage + this->dPrevLoad / 2.0); } else { dUsage = AuMax(this->dPrevLoad / 2.0, 0.0001); } } else if (!dUsage) { dUsage = AuMax(this->dPrevLoad / 2.0, 0.0001); } this->dPrevLoad = dUsage; this->bSet = true; } else if (!dDeltaProcess && !this->bSet) { // well, we obviously have some uptime. but how much? auto uDeltaNS = AuMin(dDeltaSteady, dMinSamplePeriod); if (uDeltaNS > AuMSToNS(1000 / 250)) { uDeltaNS = 100; } dUsage = uDeltaNS / dDeltaSteady2; } else if (dDeltaProcess && !this->bSet) { dUsage = dDeltaProcess / dDeltaSteady2; } else if (dDeltaProcess) { dUsage = dDeltaProcess / dDeltaSteady2; #if 0 dUsage *= dDeltaSteady2 / double(dMinSamplePeriod); if (this->dPrevLoad) dUsage = dUsage + this->dPrevLoad / 2.0; #else if (this->dPrevLoad) { auto dFrameDelta = dDeltaSteady / dMinSamplePeriod; auto dFrameDeltaInverse = 1.0 - dFrameDelta; dUsage *= dFrameDelta; dUsage += this->dPrevLoad * dFrameDeltaInverse; } else { dUsage *= dDeltaSteady / dMinSamplePeriod; } #endif } else { dUsage = this->dPrevLoad; } { dUsage = dUsage * 100.0; if (dUsage > 100.0) { return 100.0; } else if (dUsage < 0.0) { return 0.0; } else { return dUsage; } } } AUKN_SYM double GetProcessCPUUtilization() { return gProcessUsageSampler.GetLoad(); } AUKN_SYM double GetThreadCPUUtilization(AuOptional optIncludeKernel) { return tlsThreadLocalUsageSamplers[optIncludeKernel.value_or(true)].GetLoad(); } AUKN_SYM ICpuLoadSampler *CpuLoadSamplerNew(AuUInt32 uMinSamplePeriodMS, bool bThreadMode, bool bCountKernelUsage) { return _new CpuLoadSamplerImpl(uMinSamplePeriodMS, bThreadMode, bCountKernelUsage); } AUKN_SYM void CpuLoadSamplerRelease(ICpuLoadSampler *pEvent) { AuSafeDelete(pEvent); } AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, CpuLoadSampler, CpuLoadSamplerImpl, (AuUInt32, uMinSamplePeriod), (bool, bThreadMode), (bool, bCountKernelUsage)) }