mirror of
https://github.com/mikage-emu/mikage-dev.git
synced 2025-01-09 15:01:00 +01:00
141 lines
4 KiB
C++
141 lines
4 KiB
C++
#include "profiler.hpp"
|
|
|
|
#include <algorithm>
|
|
|
|
namespace Profiler {
|
|
|
|
constexpr auto skip_profiler = true;
|
|
|
|
Duration DurationMeasure::TimePassedUntil(TimePoint now) const {
|
|
return std::chrono::duration_cast<Duration>(now - begin);
|
|
}
|
|
|
|
Metric Accumulator::SnapshotFor(TimePoint now) const {
|
|
// Careful here for race-conditions!
|
|
|
|
// If paused, immediately return the metric. If paused gets toggled before returning, metric will be mildy out of date at worst.
|
|
if (paused) {
|
|
return metric;
|
|
}
|
|
|
|
// Get the current metric.
|
|
// TODO: Race condition here - measure.begin might get updated after taking the metric snapshot :/
|
|
auto snapshot_metric = metric; // TODO: Copying this should be ensured atomic!
|
|
auto snapshot_measure = measure;
|
|
|
|
auto time_passed = snapshot_measure.TimePassedUntil(now);
|
|
|
|
// If measurement begin is later than "now" (e.g. due to cross-thread
|
|
// access), assume the activity was interrupted before and hence no
|
|
// metrics were gathered.
|
|
snapshot_metric.total += std::max(time_passed, std::chrono::duration_values<Duration>::zero());
|
|
|
|
return snapshot_metric;
|
|
}
|
|
|
|
Activity& Activity::GetSubActivity(std::string_view name) {
|
|
auto it = std::find_if(sub_activities.begin(), sub_activities.end(), [=](auto& activity) { return activity.name == name; });
|
|
if (it != sub_activities.end()) {
|
|
return *it;
|
|
}
|
|
|
|
return sub_activities.emplace_back(*this, name);
|
|
}
|
|
|
|
void Activity::ResumeFrom(TimePoint time) {
|
|
if constexpr (skip_profiler) {
|
|
return;
|
|
}
|
|
|
|
if (!parent) {
|
|
// The root activity always stays active
|
|
return;
|
|
}
|
|
|
|
assert(!IsMeasuring());
|
|
|
|
// Resume parent first, just to make sure they always get a slightly higher total metric
|
|
if (!parent->IsMeasuring()) {
|
|
parent->ResumeFrom(time);
|
|
}
|
|
|
|
if (!accumulator) {
|
|
accumulator = std::make_unique<Accumulator>(metric, time);
|
|
assert(IsMeasuring());
|
|
} else {
|
|
assert(!IsMeasuring());
|
|
accumulator->Resume(time);
|
|
}
|
|
}
|
|
|
|
void Activity::InterruptAt(TimePoint time) {
|
|
if constexpr (skip_profiler) {
|
|
return;
|
|
}
|
|
|
|
// Interrupt children first, to make sure the parent always has a slightly higher total metric
|
|
for (auto& activity : sub_activities) {
|
|
if (activity.IsMeasuring()) {
|
|
activity.InterruptAt(time);
|
|
}
|
|
}
|
|
|
|
if (!IsMeasuring()) {
|
|
throw std::runtime_error("Called Interrupt on Activity " + name + " which wasn't active");
|
|
}
|
|
accumulator->Interrupt(time);
|
|
}
|
|
|
|
void Activity::Resume() {
|
|
if constexpr (skip_profiler) {
|
|
return;
|
|
}
|
|
|
|
auto time = std::chrono::steady_clock::now();
|
|
ResumeFrom(time);
|
|
}
|
|
|
|
void Activity::Interrupt() {
|
|
if constexpr (skip_profiler) {
|
|
return;
|
|
}
|
|
|
|
auto time = std::chrono::steady_clock::now();
|
|
InterruptAt(time);
|
|
}
|
|
|
|
void Activity::SwitchSubActivity(std::string_view from, std::string_view to) {
|
|
if constexpr (skip_profiler) {
|
|
return;
|
|
}
|
|
|
|
auto time = std::chrono::steady_clock::now();
|
|
GetSubActivity(from).InterruptAt(time);
|
|
GetSubActivity(to).ResumeFrom(time);
|
|
}
|
|
|
|
FrozenMetrics Activity::GetMetricsAt(TimePoint now) const {
|
|
// TODO: Race condition here. What if IsMeasuring changes before we take the snapshot?
|
|
auto metrics = IsMeasuring() ? accumulator->SnapshotFor(now) : metric;
|
|
|
|
FrozenMetrics ret { *this, metrics, {} };
|
|
// TODO: Verify iteration over std::list is thread-safe
|
|
if constexpr (!skip_profiler) {
|
|
for (auto& activity : sub_activities) {
|
|
ret.sub_metrics.emplace_back(activity.GetMetricsAt(now));
|
|
}
|
|
}
|
|
return std::move(ret);
|
|
}
|
|
|
|
FrozenMetrics Profiler::Freeze() const {
|
|
// TODO: Achieve this without interrupting any activity, and don't assume no activity is running right now!
|
|
// root_activity->Interrupt();
|
|
// root_activity->accumulator->Resume();
|
|
|
|
return root_activity->GetMetricsAt(std::chrono::steady_clock::now());
|
|
}
|
|
|
|
// TODO: MeasureScope should get the previously active activity and restore it!
|
|
|
|
} // namespace Profiler
|