yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
performance_profiler.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <iomanip>
5#include <iostream>
6#include <numeric>
7#include <sstream>
8
10#include "util/log.h"
11
12namespace yaze {
13namespace gfx {
14
16 static PerformanceProfiler instance;
17 return instance;
18}
19
20PerformanceProfiler::PerformanceProfiler() : enabled_(true), is_shutting_down_(false) {
21 // Initialize with memory pool for efficient data storage
22 // Reserve space for common operations to avoid reallocations
23 active_timers_.reserve(50);
24 operation_times_.reserve(100);
25 operation_totals_.reserve(100);
26 operation_counts_.reserve(100);
27
28 // Register destructor to set shutdown flag
29 std::atexit([]() {
30 Get().is_shutting_down_ = true;
31 });
32}
33
34void PerformanceProfiler::StartTimer(const std::string& operation_name) {
35 if (!enabled_ || is_shutting_down_) return;
36
37 active_timers_[operation_name] = std::chrono::high_resolution_clock::now();
38}
39
40void PerformanceProfiler::EndTimer(const std::string& operation_name) {
41 if (!enabled_ || is_shutting_down_) return;
42
43 auto timer_iter = active_timers_.find(operation_name);
44 if (timer_iter == active_timers_.end()) {
45 // During shutdown, silently ignore missing timers to avoid log spam
46 return;
47 }
48
49 auto end_time = std::chrono::high_resolution_clock::now();
50 auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
51 end_time - timer_iter->second).count();
52
53 double duration_ms = duration / 1000.0;
54
55 // Store timing data using memory pool for efficiency
56 operation_times_[operation_name].push_back(static_cast<double>(duration));
57 operation_totals_[operation_name] += duration_ms;
58 operation_counts_[operation_name]++;
59
60 active_timers_.erase(timer_iter);
61}
62
64 const std::string& operation_name) const {
65 TimingStats stats;
66
67 auto times_iter = operation_times_.find(operation_name);
68 auto total_iter = operation_totals_.find(operation_name);
69
70 if (times_iter == operation_times_.end() || times_iter->second.empty()) {
71 return stats;
72 }
73
74 const auto& times = times_iter->second;
75 stats.sample_count = times.size();
76 stats.total_time_ms = (total_iter != operation_totals_.end()) ? total_iter->second : 0.0;
77
78 if (times.empty()) {
79 return stats;
80 }
81
82 // Calculate min, max, and average
83 stats.min_time_us = *std::min_element(times.begin(), times.end());
84 stats.max_time_us = *std::max_element(times.begin(), times.end());
85 stats.avg_time_us = std::accumulate(times.begin(), times.end(), 0.0) / times.size();
86
87 // Calculate median
88 std::vector<double> sorted_times = times;
89 std::sort(sorted_times.begin(), sorted_times.end());
91
92 return stats;
93}
94
95std::string PerformanceProfiler::GenerateReport(bool log_to_sdl) const {
96 std::ostringstream report;
97 report << "\n=== YAZE Unified Performance Report ===\n";
98 report << "Total Operations Tracked: " << operation_times_.size() << "\n";
99 report << "Performance Monitoring: " << (enabled_ ? "ENABLED" : "DISABLED") << "\n\n";
100
101 // Memory pool statistics
102 auto [used_bytes, total_bytes] = MemoryPool::Get().GetMemoryStats();
103 report << "Memory Pool Usage: " << std::fixed << std::setprecision(2)
104 << (used_bytes / (1024.0 * 1024.0)) << " MB / "
105 << (total_bytes / (1024.0 * 1024.0)) << " MB\n\n";
106
107 for (const auto& [operation, times] : operation_times_) {
108 if (times.empty()) continue;
109
110 auto stats = GetStats(operation);
111 report << "Operation: " << operation << "\n";
112 report << " Samples: " << stats.sample_count << "\n";
113 report << " Min: " << std::fixed << std::setprecision(2) << stats.min_time_us << " μs\n";
114 report << " Max: " << std::fixed << std::setprecision(2) << stats.max_time_us << " μs\n";
115 report << " Average: " << std::fixed << std::setprecision(2) << stats.avg_time_us << " μs\n";
116 report << " Median: " << std::fixed << std::setprecision(2) << stats.median_time_us << " μs\n";
117 report << " Total: " << std::fixed << std::setprecision(2) << stats.total_time_ms << " ms\n";
118
119 // Performance analysis
120 if (operation.find("palette_lookup") != std::string::npos) {
121 if (stats.avg_time_us < 1.0) {
122 report << " Status: ✓ OPTIMIZED (O(1) hash map lookup)\n";
123 } else {
124 report << " Status: ⚠ NEEDS OPTIMIZATION (O(n) linear search)\n";
125 }
126 } else if (operation.find("texture_update") != std::string::npos) {
127 if (stats.avg_time_us < 100.0) {
128 report << " Status: ✓ OPTIMIZED (dirty region tracking)\n";
129 } else {
130 report << " Status: ⚠ NEEDS OPTIMIZATION (full texture updates)\n";
131 }
132 } else if (operation.find("tile_cache") != std::string::npos) {
133 if (stats.avg_time_us < 10.0) {
134 report << " Status: ✓ OPTIMIZED (LRU cache hit)\n";
135 } else {
136 report << " Status: ⚠ CACHE MISS (tile recreation needed)\n";
137 }
138 } else if (operation.find("::Load") != std::string::npos) {
139 double avg_time_ms = stats.avg_time_us / 1000.0;
140 if (avg_time_ms < 100.0) {
141 report << " Status: ✓ FAST LOADING (< 100ms)\n";
142 } else if (avg_time_ms < 1000.0) {
143 report << " Status: ⚠ MODERATE LOADING (100-1000ms)\n";
144 } else {
145 report << " Status: ⚠ SLOW LOADING (> 1000ms)\n";
146 }
147 }
148
149 report << "\n";
150 }
151
152 // Overall performance summary
153 report << "=== Performance Summary ===\n";
154 size_t total_samples = 0;
155 double total_time = 0.0;
156
157 for (const auto& [operation, times] : operation_times_) {
158 total_samples += times.size();
159 total_time += std::accumulate(times.begin(), times.end(), 0.0);
160 }
161
162 if (total_samples > 0) {
163 report << "Total Samples: " << total_samples << "\n";
164 report << "Total Time: " << std::fixed << std::setprecision(2)
165 << total_time / 1000.0 << " ms\n";
166 report << "Average Time per Operation: " << std::fixed << std::setprecision(2)
167 << total_time / total_samples << " μs\n";
168 }
169
170 std::string report_str = report.str();
171
172 if (log_to_sdl) {
173 SDL_Log("%s", report_str.c_str());
174 }
175
176 return report_str;
177}
178
180 active_timers_.clear();
181 operation_times_.clear();
182 operation_totals_.clear();
183 operation_counts_.clear();
184}
185
186void PerformanceProfiler::ClearOperation(const std::string& operation_name) {
187 active_timers_.erase(operation_name);
188 operation_times_.erase(operation_name);
189 operation_totals_.erase(operation_name);
190 operation_counts_.erase(operation_name);
191}
192
193std::vector<std::string> PerformanceProfiler::GetOperationNames() const {
194 std::vector<std::string> names;
195 names.reserve(operation_times_.size());
196 for (const auto& [name, times] : operation_times_) {
197 names.push_back(name);
198 }
199 return names;
200}
201
202bool PerformanceProfiler::IsTiming(const std::string& operation_name) const {
203 return active_timers_.find(operation_name) != active_timers_.end();
204}
205
206double PerformanceProfiler::GetAverageTime(const std::string& operation_name) const {
207 auto total_it = operation_totals_.find(operation_name);
208 auto count_it = operation_counts_.find(operation_name);
209
210 if (total_it == operation_totals_.end() || count_it == operation_counts_.end() ||
211 count_it->second == 0) {
212 return 0.0;
213 }
214
215 return total_it->second / count_it->second;
216}
217
218double PerformanceProfiler::GetTotalTime(const std::string& operation_name) const {
219 auto total_it = operation_totals_.find(operation_name);
220 return (total_it != operation_totals_.end()) ? total_it->second : 0.0;
221}
222
223int PerformanceProfiler::GetOperationCount(const std::string& operation_name) const {
224 auto count_it = operation_counts_.find(operation_name);
225 return (count_it != operation_counts_.end()) ? count_it->second : 0;
226}
227
229 std::cout << "\n=== Performance Summary ===\n";
230 std::cout << std::left << std::setw(30) << "Operation"
231 << std::setw(12) << "Count"
232 << std::setw(15) << "Total (ms)"
233 << std::setw(15) << "Average (ms)" << "\n";
234 std::cout << std::string(72, '-') << "\n";
235
236 for (const auto& [operation_name, times] : operation_times_) {
237 if (times.empty()) continue;
238
239 auto total_it = operation_totals_.find(operation_name);
240 auto count_it = operation_counts_.find(operation_name);
241
242 if (total_it != operation_totals_.end() && count_it != operation_counts_.end()) {
243 double total_time = total_it->second;
244 int count = count_it->second;
245 double avg_time = (count > 0) ? total_time / count : 0.0;
246
247 std::cout << std::left << std::setw(30) << operation_name
248 << std::setw(12) << count
249 << std::setw(15) << std::fixed << std::setprecision(2) << total_time
250 << std::setw(15) << std::fixed << std::setprecision(2) << avg_time
251 << "\n";
252 }
253 }
254 std::cout << std::string(72, '-') << "\n";
255}
256
257double PerformanceProfiler::CalculateMedian(std::vector<double> values) {
258 if (values.empty()) {
259 return 0.0;
260 }
261
262 size_t size = values.size();
263 if (size % 2 == 0) {
264 return (values[size / 2 - 1] + values[size / 2]) / 2.0;
265 }
266 return values[size / 2];
267}
268
269// ScopedTimer implementation
270ScopedTimer::ScopedTimer(const std::string& operation_name)
271 : operation_name_(operation_name) {
274 }
275}
276
278 // Check if profiler is still valid (not shutting down) to prevent
279 // crashes during static destruction order issues
282 }
283}
284
285} // namespace gfx
286} // namespace yaze
std::pair< size_t, size_t > GetMemoryStats() const
Get memory usage statistics.
static MemoryPool & Get()
Unified performance profiler for all YAZE operations.
void Clear()
Clear all timing data.
void StartTimer(const std::string &operation_name)
Start timing an operation.
double GetAverageTime(const std::string &operation_name) const
Get the average time for an operation in milliseconds.
std::vector< std::string > GetOperationNames() const
Get list of all tracked operations.
TimingStats GetStats(const std::string &operation_name) const
void ClearOperation(const std::string &operation_name)
Clear timing data for a specific operation.
static PerformanceProfiler & Get()
int GetOperationCount(const std::string &operation_name) const
Get the number of times an operation was measured.
static double CalculateMedian(std::vector< double > values)
Calculate median value from a sorted vector.
std::string GenerateReport(bool log_to_sdl=true) const
Generate a comprehensive performance report.
void EndTimer(const std::string &operation_name)
End timing an operation.
std::unordered_map< std::string, int > operation_counts_
std::unordered_map< std::string, TimePoint > active_timers_
static bool IsValid()
Check if the profiler is in a valid state (not shutting down) This prevents crashes during static des...
static bool IsEnabled()
Check if performance monitoring is enabled.
std::unordered_map< std::string, double > operation_totals_
std::unordered_map< std::string, std::vector< double > > operation_times_
double GetTotalTime(const std::string &operation_name) const
Get the total time for an operation in milliseconds.
void PrintSummary() const
Print a summary of all operations to console.
bool IsTiming(const std::string &operation_name) const
Check if an operation is currently being timed.
ScopedTimer(const std::string &operation_name)
Main namespace for the application.
Get timing statistics for an operation.