summaryrefslogtreecommitdiff
path: root/src/common/profiler.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/profiler.h')
-rw-r--r--src/common/profiler.h134
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 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <atomic>
8#include <chrono>
9
10#include "common/assert.h"
11#include "common/thread.h"
12
13namespace Common {
14namespace Profiling {
15
16// If this is defined to 0, it turns all Timers into no-ops.
17#ifndef ENABLE_PROFILING
18#define ENABLE_PROFILING 1
19#endif
20
21using Duration = std::chrono::nanoseconds;
22using Clock = std::chrono::high_resolution_clock;
23
24/**
25 * Represents a timing category that measured time can be accounted towards. Should be declared as a
26 * global variable and passed to Timers.
27 */
28class TimingCategory final {
29public:
30 TimingCategory(const char* name, TimingCategory* parent = nullptr);
31
32 unsigned int GetCategoryId() const {
33 return category_id;
34 }
35
36 /// Adds some time to this category. Can safely be called from multiple threads at the same time.
37 void AddTime(Duration amount) {
38 std::atomic_fetch_add_explicit(
39 &accumulated_duration, amount.count(),
40 std::memory_order_relaxed);
41 }
42
43 /**
44 * Atomically retrieves the accumulated measured time for this category and resets the counter
45 * to zero. Can be safely called concurrently with AddTime.
46 */
47 Duration GetAccumulatedTime() {
48 return Duration(std::atomic_exchange_explicit(
49 &accumulated_duration, (Duration::rep)0,
50 std::memory_order_relaxed));
51 }
52
53private:
54 unsigned int category_id;
55 std::atomic<Duration::rep> accumulated_duration;
56};
57
58/**
59 * Measures time elapsed between a call to Start and a call to Stop and attributes it to the given
60 * TimingCategory. Start/Stop can be called multiple times on the same timer, but each call must be
61 * appropriately paired.
62 *
63 * When a Timer is started, it automatically pauses a previously running timer on the same thread,
64 * which is resumed when it is stopped. As such, no special action needs to be taken to avoid
65 * double-accounting of time on two categories.
66 */
67class Timer {
68public:
69 Timer(TimingCategory& category) : category(category) {
70 }
71
72 void Start() {
73#if ENABLE_PROFILING
74 ASSERT(!running);
75 previous_timer = current_timer;
76 current_timer = this;
77 if (previous_timer != nullptr)
78 previous_timer->StopTiming();
79
80 StartTiming();
81#endif
82 }
83
84 void Stop() {
85#if ENABLE_PROFILING
86 ASSERT(running);
87 StopTiming();
88
89 if (previous_timer != nullptr)
90 previous_timer->StartTiming();
91 current_timer = previous_timer;
92#endif
93 }
94
95private:
96#if ENABLE_PROFILING
97 void StartTiming() {
98 start = Clock::now();
99 running = true;
100 }
101
102 void StopTiming() {
103 auto duration = Clock::now() - start;
104 running = false;
105 category.AddTime(std::chrono::duration_cast<Duration>(duration));
106 }
107
108 Clock::time_point start;
109 bool running = false;
110
111 Timer* previous_timer;
112 static thread_local Timer* current_timer;
113#endif
114
115 TimingCategory& category;
116};
117
118/**
119 * A Timer that automatically starts timing when created and stops at the end of the scope. Should
120 * be used in the majority of cases.
121 */
122class ScopeTimer : public Timer {
123public:
124 ScopeTimer(TimingCategory& category) : Timer(category) {
125 Start();
126 }
127
128 ~ScopeTimer() {
129 Stop();
130 }
131};
132
133} // namespace Profiling
134} // namespace Common