diff options
author | Yuri Kunde Schlesner <yuriks@yuriks.net> | 2015-02-05 17:53:25 +0100 |
---|---|---|
committer | Yuri Kunde Schlesner <yuriks@yuriks.net> | 2015-03-02 01:47:13 +0100 |
commit | cd1fbfcf1b70e365d81480ec0f56db19ed02454f (patch) | |
tree | b220b105d1b8016bb258047683bf2d03795c8881 /src/common/profiler.h | |
parent | Merge pull request #616 from archshift/5551 (diff) | |
download | yuzu-cd1fbfcf1b70e365d81480ec0f56db19ed02454f.tar yuzu-cd1fbfcf1b70e365d81480ec0f56db19ed02454f.tar.gz yuzu-cd1fbfcf1b70e365d81480ec0f56db19ed02454f.tar.bz2 yuzu-cd1fbfcf1b70e365d81480ec0f56db19ed02454f.tar.lz yuzu-cd1fbfcf1b70e365d81480ec0f56db19ed02454f.tar.xz yuzu-cd1fbfcf1b70e365d81480ec0f56db19ed02454f.tar.zst yuzu-cd1fbfcf1b70e365d81480ec0f56db19ed02454f.zip |
Diffstat (limited to 'src/common/profiler.h')
-rw-r--r-- | src/common/profiler.h | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/src/common/profiler.h b/src/common/profiler.h new file mode 100644 index 000000000..53c4f6eaf --- /dev/null +++ b/src/common/profiler.h @@ -0,0 +1,134 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <atomic> +#include <chrono> + +#include "common/assert.h" +#include "common/thread.h" + +namespace Common { +namespace Profiling { + +// If this is defined to 0, it turns all Timers into no-ops. +#ifndef ENABLE_PROFILING +#define ENABLE_PROFILING 1 +#endif + +using Duration = std::chrono::nanoseconds; +using Clock = std::chrono::high_resolution_clock; + +/** + * Represents a timing category that measured time can be accounted towards. Should be declared as a + * global variable and passed to Timers. + */ +class TimingCategory final { +public: + TimingCategory(const char* name, TimingCategory* parent = nullptr); + + unsigned int GetCategoryId() const { + return category_id; + } + + /// Adds some time to this category. Can safely be called from multiple threads at the same time. + void AddTime(Duration amount) { + std::atomic_fetch_add_explicit( + &accumulated_duration, amount.count(), + std::memory_order_relaxed); + } + + /** + * Atomically retrieves the accumulated measured time for this category and resets the counter + * to zero. Can be safely called concurrently with AddTime. + */ + Duration GetAccumulatedTime() { + return Duration(std::atomic_exchange_explicit( + &accumulated_duration, (Duration::rep)0, + std::memory_order_relaxed)); + } + +private: + unsigned int category_id; + std::atomic<Duration::rep> accumulated_duration; +}; + +/** + * Measures time elapsed between a call to Start and a call to Stop and attributes it to the given + * TimingCategory. Start/Stop can be called multiple times on the same timer, but each call must be + * appropriately paired. + * + * When a Timer is started, it automatically pauses a previously running timer on the same thread, + * which is resumed when it is stopped. As such, no special action needs to be taken to avoid + * double-accounting of time on two categories. + */ +class Timer { +public: + Timer(TimingCategory& category) : category(category) { + } + + void Start() { +#if ENABLE_PROFILING + ASSERT(!running); + previous_timer = current_timer; + current_timer = this; + if (previous_timer != nullptr) + previous_timer->StopTiming(); + + StartTiming(); +#endif + } + + void Stop() { +#if ENABLE_PROFILING + ASSERT(running); + StopTiming(); + + if (previous_timer != nullptr) + previous_timer->StartTiming(); + current_timer = previous_timer; +#endif + } + +private: +#if ENABLE_PROFILING + void StartTiming() { + start = Clock::now(); + running = true; + } + + void StopTiming() { + auto duration = Clock::now() - start; + running = false; + category.AddTime(std::chrono::duration_cast<Duration>(duration)); + } + + Clock::time_point start; + bool running = false; + + Timer* previous_timer; + static thread_local Timer* current_timer; +#endif + + TimingCategory& category; +}; + +/** + * A Timer that automatically starts timing when created and stops at the end of the scope. Should + * be used in the majority of cases. + */ +class ScopeTimer : public Timer { +public: + ScopeTimer(TimingCategory& category) : Timer(category) { + Start(); + } + + ~ScopeTimer() { + Stop(); + } +}; + +} // namespace Profiling +} // namespace Common |