Files
op-pedal/src/CpuUse.hpp
T
2026-06-02 12:59:41 -04:00

173 lines
5.1 KiB
C++

/*
* MIT License
*
* Copyright (c) Robin E.R. Davies
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <chrono>
#include <mutex>
namespace pipedal
{
enum ProfileCategory
{
Init,
Read,
Driver,
Execute,
Write,
MaxCategory
};
constexpr size_t NUM_PROFILE_CATEGORIES = (size_t)ProfileCategory::MaxCategory;
class CpuUse
{
private:
using ClockT = std::chrono::steady_clock;
using TimeT = ClockT::time_point;
using DurationT = ClockT::duration;
using SampleT = DurationT::rep;
public:
class CpuUseAverager
{
private:
static constexpr size_t NUM_SAMPLES = 20; // average over N samples.
SampleT sampleTotal = 0;
SampleT samples[NUM_SAMPLES];
size_t sampleIndex = 0;
public:
CpuUseAverager();
SampleT GetAverage() const
{
return sampleTotal / NUM_SAMPLES;
}
SampleT GetTotal() const {
return sampleTotal;
}
void AddSample(DurationT duration)
{
SampleT sample = duration.count();
sampleTotal = sampleTotal + sample-samples[sampleIndex];
samples[sampleIndex] = sample;
++sampleIndex;
if (sampleIndex >= NUM_SAMPLES)
{
sampleIndex = 0;
}
}
};
private:
std::mutex sync;
TimeT lastSample;
CpuUseAverager profileTimes[NUM_PROFILE_CATEGORIES];
float currentCpuUse = 0;
float currentOverhead = 0;
CpuUseAverager &GetCategory(ProfileCategory category) {
return profileTimes[(size_t)category];
}
public:
TimeT Now() {
return ClockT::now();
}
void SetStartTime(TimeT time)
{
lastSample = time;
}
void AddSample(ProfileCategory category, TimeT time)
{
profileTimes[(size_t)category].AddSample((time-lastSample));
lastSample = time;
}
void AddSample(ProfileCategory category)
{
AddSample(category,Now());
}
void AddSample(ProfileCategory category, TimeT startTime, TimeT endTime)
{
profileTimes[(size_t)category].AddSample((endTime-startTime));
}
void UpdateCpuUse() {
SampleT readTime = GetCategory(ProfileCategory::Read).GetTotal();
SampleT writeTime = GetCategory(ProfileCategory::Driver).GetTotal();
SampleT processingTime = GetCategory(ProfileCategory::Execute).GetTotal() + GetCategory(ProfileCategory::Driver).GetTotal();
// most of the waiting occurs in the write. Assume that the difference between readTime and write Time is how long we spent waiting (i.e. free time)
SampleT waitTime;
SampleT overheadTime;
if (readTime > writeTime)
{
waitTime = readTime-writeTime;
overheadTime = writeTime;
} else {
waitTime = writeTime-readTime;
overheadTime = readTime;
}
SampleT totalTime = writeTime + readTime + processingTime;
SampleT maxTime = waitTime + processingTime;
float result = 0.0f;
float overhead = 0.0f;
if (maxTime != 0 && totalTime != 0)
{
result = 100.0f * (processingTime) / (maxTime);
overhead = 100.0f * (overheadTime * 2) / totalTime;
}
{
std::lock_guard lock{sync};
currentCpuUse = result;
currentOverhead = overhead;
}
}
float GetCpuUse()
{
std::lock_guard lock { sync};
return currentCpuUse;
}
float GetCpuOverhead()
{
std::lock_guard lock { sync};
return currentOverhead;
}
};
}