载入中...
搜索中...
未找到
Profile.cpp
浏览该文件的文档.
1#include "common/Profile.h"
2
3#include <algorithm>
4#include <chrono>
5#include <cstdio>
6#include <cstring>
7#include <mutex>
8#include <sstream>
9#include <thread>
10#include <unordered_map>
11#include <vector>
12
13namespace eve::prof {
14
15namespace {
16std::atomic<bool> g_enabled{false};
17
18int64_t nowNs() {
19 return std::chrono::duration_cast<std::chrono::nanoseconds>(
20 std::chrono::steady_clock::now().time_since_epoch())
21 .count();
22}
23
24// Module id interning (thread-safe).
25std::mutex g_modulesMutex;
26std::vector<std::string> g_moduleNames{""}; // index 0 = "default"
27std::unordered_map<std::string, uint32_t> g_moduleIds;
28
29uint64_t zoneKey(uint32_t module, const char* name) {
30 return (static_cast<uint64_t>(module) << 32) ^ std::hash<const char*>{}(name);
31}
32} // namespace
33
35 uint32_t threadId = 0;
36 std::string threadName = "main";
37 std::vector<ZoneRecord> records;
38 std::vector<int32_t> stack; // indices of open zones
39};
40
42 int64_t beginNs = 0;
43 int64_t endNs = 0;
44 std::vector<ZoneSample> samples;
45};
46
47namespace {
48std::mutex g_threadsMutex;
49} // namespace
50
51Profiler::ThreadBuffer& Profiler::currentBuffer() {
52 static thread_local ThreadBuffer tb;
53 if (tb.threadId == 0) {
54 tb.threadId =
55 static_cast<uint32_t>(std::hash<std::thread::id>{}(std::this_thread::get_id()));
56 const bool isMain = std::this_thread::get_id() == std::thread::id();
57 tb.threadName = isMain ? "main" : "worker";
58 std::lock_guard<std::mutex> lock(g_threadsMutex);
59 threads().push_back(&tb);
60 }
61 return tb;
62}
63
64std::vector<Profiler::ThreadBuffer*>& Profiler::threads() {
65 static std::vector<ThreadBuffer*> t;
66 return t;
67}
68
69Profiler::Frame& Profiler::lastFrameStorage() {
70 static Frame f;
71 return f;
72}
73
74bool& Profiler::hasFrameFlag() {
75 static bool b = false;
76 return b;
77}
78
79uint32_t Profiler::moduleId(const char* module) {
80 if (!module || !module[0]) return 0;
81 std::lock_guard<std::mutex> lock(g_modulesMutex);
82 auto it = g_moduleIds.find(module);
83 if (it != g_moduleIds.end()) return it->second;
84 const uint32_t id = static_cast<uint32_t>(g_moduleNames.size());
85 g_moduleNames.push_back(module);
86 g_moduleIds.emplace(module, id);
87 return id;
88}
89
90// --- public API -------------------------------------------------------------
91
92void Profiler::setEnabled(bool on) { g_enabled.store(on, std::memory_order_relaxed); }
93
94bool Profiler::enabled() { return g_enabled.load(std::memory_order_relaxed); }
95
96void Profiler::zoneBegin(const char* name, const char* module) {
97 if (!g_enabled.load(std::memory_order_relaxed)) return;
98 ThreadBuffer& tb = currentBuffer();
100 r.name = name ? name : "";
101 r.module = moduleId(module);
102 r.threadId = tb.threadId;
103 r.parent = tb.stack.empty() ? -1 : tb.stack.back();
104 r.depth = static_cast<int16_t>(tb.stack.size());
105 r.startNs = nowNs();
106 r.endNs = 0;
107 r.kind = 0;
108 const int32_t idx = static_cast<int32_t>(tb.records.size());
109 tb.records.push_back(r);
110 tb.stack.push_back(idx);
111}
112
114 if (!g_enabled.load(std::memory_order_relaxed)) return;
115 ThreadBuffer& tb = currentBuffer();
116 if (tb.stack.empty()) return;
117 const int32_t idx = tb.stack.back();
118 tb.stack.pop_back();
119 tb.records[static_cast<size_t>(idx)].endNs = nowNs();
120}
121
123 std::lock_guard<std::mutex> lock(g_threadsMutex);
124 for (auto* tb : threads()) {
125 tb->records.clear();
126 tb->stack.clear();
127 }
128 lastFrameStorage() = Frame{};
129 hasFrameFlag() = false;
130}
131
133 if (!g_enabled.load(std::memory_order_relaxed)) return 0.0;
134
135 const int64_t t0 = nowNs();
136
137 // Snapshot and clear every thread buffer.
138 struct Snap {
139 uint32_t tid;
140 std::string tname;
141 std::vector<ZoneRecord> records;
142 };
143 std::vector<Snap> snaps;
144 {
145 std::lock_guard<std::mutex> lock(g_threadsMutex);
146 for (auto* tb : threads()) {
147 Snap s;
148 s.tid = tb->threadId;
149 s.tname = tb->threadName;
150 for (auto& r : tb->records)
151 if (r.endNs == 0) r.endNs = t0;
152 s.records = std::move(tb->records);
153 tb->records.clear();
154 tb->stack.clear();
155 snaps.push_back(std::move(s));
156 }
157 }
158
159 // Aggregate per (module, name): self = own total - sum of children totals.
160 struct Agg {
161 double selfMs = 0.0;
162 double totalMs = 0.0;
163 int count = 0;
164 int minDepth = 0x7fffffff;
165 uint32_t module = 0;
166 std::string name;
167 std::string thread;
168 };
169 std::unordered_map<uint64_t, Agg> aggMap;
170
171 for (const auto& s : snaps) {
172 const size_t n = s.records.size();
173 std::vector<int64_t> total(n, 0);
174 std::vector<int64_t> child(n, 0);
175 for (size_t i = 0; i < n; ++i) {
176 int64_t t = s.records[i].endNs - s.records[i].startNs;
177 if (t < 0) t = 0;
178 total[i] = t;
179 }
180 for (size_t i = 0; i < n; ++i) {
181 const int32_t p = s.records[i].parent;
182 if (p >= 0 && p < static_cast<int32_t>(n)) child[static_cast<size_t>(p)] += total[i];
183 }
184 for (size_t i = 0; i < n; ++i) {
185 const auto& r = s.records[i];
186 const int64_t self = total[i] - child[i];
187 Agg& a = aggMap[zoneKey(r.module, r.name)];
188 a.selfMs += static_cast<double>(self) / 1'000'000.0;
189 a.totalMs += static_cast<double>(total[i]) / 1'000'000.0;
190 ++a.count;
191 a.module = r.module;
192 a.name = r.name ? r.name : "";
193 a.thread = s.tname;
194 if (r.depth < a.minDepth) a.minDepth = r.depth;
195 }
196 }
197
198 Frame f;
199 f.beginNs = t0;
200 for (auto& kv : aggMap) {
201 Agg& a = kv.second;
202 ZoneSample sm;
203 sm.module = a.module < g_moduleNames.size() ? g_moduleNames[a.module]
204 : std::string("default");
205 sm.name = a.name;
206 sm.thread = a.thread;
207 sm.selfMs = a.selfMs;
208 sm.totalMs = a.totalMs;
209 sm.count = a.count;
210 sm.minDepth = a.minDepth;
211 f.samples.push_back(std::move(sm));
212 }
213 std::sort(f.samples.begin(), f.samples.end(),
214 [](const ZoneSample& x, const ZoneSample& y) { return x.selfMs > y.selfMs; });
215 f.endNs = nowNs();
216
217 lastFrameStorage() = std::move(f);
218 hasFrameFlag() = true;
219 return static_cast<double>(lastFrameStorage().endNs - lastFrameStorage().beginNs) / 1'000'000.0;
220}
221
222bool Profiler::hasFrame() { return hasFrameFlag(); }
223
224const std::vector<ZoneSample>& Profiler::lastFrame() { return lastFrameStorage().samples; }
225
226std::string Profiler::textReport() {
227 if (!hasFrameFlag()) return "=== Profiler: no frame captured yet ===\n";
228 std::ostringstream os;
229 char buf[64];
230 const double totalMs =
231 static_cast<double>(lastFrameStorage().endNs - lastFrameStorage().beginNs) / 1'000'000.0;
232 std::snprintf(buf, sizeof(buf), "%.2f", totalMs);
233 os << "=== Profiler frame (" << buf << " ms) ===\n";
234 os << "-- Per-module / per-zone (self time, sorted) --\n";
235 for (const auto& s : lastFrameStorage().samples) {
236 std::snprintf(buf, sizeof(buf), "%.3f", s.selfMs);
237 os << " [" << s.module << "/" << s.thread << "] " << s.name
238 << " self=" << buf << " ms";
239 std::snprintf(buf, sizeof(buf), "%.3f", s.totalMs);
240 os << " total=" << buf << " ms";
241 if (s.count > 1) os << " x" << s.count;
242 os << "\n";
243 }
244 return os.str();
245}
246
247} // namespace eve::prof
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
const std::string & s
glm::vec4 p[6]
glm::vec3 n
Definition Grass.cpp:63
double r
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float tb
int idx
float f
std::string id
Definition PlayHost.cpp:108
float t
std::uint32_t count
static std::string textReport()
Human-readable report of the last frame.
Definition Profile.cpp:226
static void setEnabled(bool on)
Turns collection on/off globally (off = single-branch no-op).
Definition Profile.cpp:92
static const std::vector< ZoneSample > & lastFrame()
Per-module/per-zone samples of the last frame, sorted by self time.
Definition Profile.cpp:224
static void zoneBegin(const char *name, const char *module=nullptr)
Opens a scoped zone on the current thread.
Definition Profile.cpp:96
static double frameMark()
Finalizes the current frame: snapshots all thread buffers and aggregates them into lastFrame().
Definition Profile.cpp:132
static void reset()
Clears all collected state.
Definition Profile.cpp:122
static uint32_t moduleId(const char *module)
Interns a module name and returns its stable id.
Definition Profile.cpp:79
static void zoneEnd()
Closes the innermost open zone on the current thread.
Definition Profile.cpp:113
static bool hasFrame()
True once at least one frame has been aggregated.
Definition Profile.cpp:222
static bool enabled()
True when collection is active.
Definition Profile.cpp:94
std::vector< ZoneSample > samples
Definition Profile.cpp:44
std::vector< int32_t > stack
Definition Profile.cpp:38
std::vector< ZoneRecord > records
Definition Profile.cpp:37
One completed profiling zone (pre-order, parent-linked within a thread).
Definition Profile.h:35
const char * name
Definition Profile.h:36
Per-module/per-zone aggregate of the last completed frame.
Definition Profile.h:49
std::string thread
Definition Profile.h:52
std::string std::string name
Definition Profile.h:51