Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
perf_counters.hpp
Go to the documentation of this file.
1#pragma once
2
3#if defined(PULP_GPU_RUNTIME)
4// This build's device layer is gpu-runtime's (tools/cmake/PulpGpuRuntime.cmake):
5// these are its types and functions under their Pulp names, so a Pulp object
6// and a gpu-runtime object are one and the same.
7#if defined(PULP_BENCHMARK) && !defined(GPU_RUNTIME_BENCHMARK)
8#error "PULP_BENCHMARK needs a gpu-runtime built with GPU_RUNTIME_BENCHMARK"
9#endif
10#if defined(GPU_RUNTIME_BENCHMARK)
11#include <gpu_runtime/bench/perf_counters.hpp>
12
13namespace pulp::render::bench {
14using gpu_runtime::bench::now_us;
15using gpu_runtime::bench::PerfCounters;
16} // namespace pulp::render::bench
17#endif // GPU_RUNTIME_BENCHMARK
18#else // !PULP_GPU_RUNTIME
19
40
41#ifdef PULP_BENCHMARK
42
43#include <atomic>
44#include <chrono>
45
46namespace pulp::render::bench {
47
51static inline double now_us() {
52 using Clock = std::chrono::high_resolution_clock;
53 return static_cast<double>(
54 std::chrono::duration_cast<std::chrono::nanoseconds>(
55 Clock::now().time_since_epoch()).count()) / 1000.0;
56}
57
63struct PerfCounters {
64 // Per-frame latency buckets (microseconds, accumulated across samples).
65 std::atomic<double> audio_copy_total_us{0.0};
66 std::atomic<double> triplebuffer_publish_total_us{0.0};
67 std::atomic<double> gpu_upload_total_us{0.0};
68 std::atomic<double> gpu_readback_total_us{0.0};
69 std::atomic<double> gpu_dispatch_total_us{0.0};
70 std::atomic<double> total_frame_total_us{0.0};
71
72 // Per-frame bandwidth buckets (bytes, accumulated across samples).
73 std::atomic<double> cpu_to_gpu_bytes_total{0.0};
74 std::atomic<double> gpu_to_cpu_bytes_total{0.0};
75
76 // Number of samples (frames) rolled into the above totals.
77 std::atomic<double> sample_count{0.0};
78
79 // JS→GPU upload instrumentation. Captures the JS-driven WebGPU resource
80 // setup path in `core/view/src/widget_bridge.cpp`: base64 decode cost,
81 // the number of WriteBuffer calls (so per-upload cost is derivable from
82 // `gpu_upload_total_us / gpu_buffer_upload_count`), and the peak resident
83 // GPU-buffer memory so we can tell whether a workload actually stresses
84 // the bridge.
85 //
86 // The peak is tracked via a `compare_exchange` loop in
87 // `observe_resident_peak` so concurrent writers stay monotonic
88 // without a lock.
89 std::atomic<double> base64_decode_total_us{0.0};
90 std::atomic<double> gpu_buffer_upload_count{0.0};
91 std::atomic<double> gpu_buffer_bytes_resident_peak{0.0};
92
94 void reset() {
95 audio_copy_total_us.store(0.0, std::memory_order_relaxed);
96 triplebuffer_publish_total_us.store(0.0, std::memory_order_relaxed);
97 gpu_upload_total_us.store(0.0, std::memory_order_relaxed);
98 gpu_readback_total_us.store(0.0, std::memory_order_relaxed);
99 gpu_dispatch_total_us.store(0.0, std::memory_order_relaxed);
100 total_frame_total_us.store(0.0, std::memory_order_relaxed);
101 cpu_to_gpu_bytes_total.store(0.0, std::memory_order_relaxed);
102 gpu_to_cpu_bytes_total.store(0.0, std::memory_order_relaxed);
103 sample_count.store(0.0, std::memory_order_relaxed);
104 base64_decode_total_us.store(0.0, std::memory_order_relaxed);
105 gpu_buffer_upload_count.store(0.0, std::memory_order_relaxed);
106 gpu_buffer_bytes_resident_peak.store(0.0, std::memory_order_relaxed);
107 }
108
112 void observe_resident_peak(double candidate) {
113 double prev = gpu_buffer_bytes_resident_peak.load(std::memory_order_relaxed);
114 while (candidate > prev &&
115 !gpu_buffer_bytes_resident_peak.compare_exchange_weak(
116 prev, candidate, std::memory_order_relaxed)) {
117 // `prev` is refreshed by `compare_exchange_weak` on failure;
118 // loop until we either succeed or the candidate is no
119 // longer larger.
120 }
121 }
122
126 struct Snapshot {
127 double audio_copy_total_us = 0.0;
128 double triplebuffer_publish_total_us = 0.0;
129 double gpu_upload_total_us = 0.0;
130 double gpu_readback_total_us = 0.0;
131 double gpu_dispatch_total_us = 0.0;
132 double total_frame_total_us = 0.0;
133 double cpu_to_gpu_bytes_total = 0.0;
134 double gpu_to_cpu_bytes_total = 0.0;
135 double sample_count = 0.0;
136 double base64_decode_total_us = 0.0;
137 double gpu_buffer_upload_count = 0.0;
138 double gpu_buffer_bytes_resident_peak = 0.0;
139 };
140
145 Snapshot snapshot_and_reset() {
146 Snapshot s;
147 s.audio_copy_total_us =
148 audio_copy_total_us.exchange(0.0, std::memory_order_relaxed);
149 s.triplebuffer_publish_total_us =
150 triplebuffer_publish_total_us.exchange(0.0, std::memory_order_relaxed);
151 s.gpu_upload_total_us =
152 gpu_upload_total_us.exchange(0.0, std::memory_order_relaxed);
153 s.gpu_readback_total_us =
154 gpu_readback_total_us.exchange(0.0, std::memory_order_relaxed);
155 s.gpu_dispatch_total_us =
156 gpu_dispatch_total_us.exchange(0.0, std::memory_order_relaxed);
157 s.total_frame_total_us =
158 total_frame_total_us.exchange(0.0, std::memory_order_relaxed);
159 s.cpu_to_gpu_bytes_total =
160 cpu_to_gpu_bytes_total.exchange(0.0, std::memory_order_relaxed);
161 s.gpu_to_cpu_bytes_total =
162 gpu_to_cpu_bytes_total.exchange(0.0, std::memory_order_relaxed);
163 s.sample_count =
164 sample_count.exchange(0.0, std::memory_order_relaxed);
165 s.base64_decode_total_us =
166 base64_decode_total_us.exchange(0.0, std::memory_order_relaxed);
167 s.gpu_buffer_upload_count =
168 gpu_buffer_upload_count.exchange(0.0, std::memory_order_relaxed);
169 s.gpu_buffer_bytes_resident_peak =
170 gpu_buffer_bytes_resident_peak.exchange(0.0, std::memory_order_relaxed);
171 return s;
172 }
173};
174
175} // namespace pulp::render::bench
176
177#endif // PULP_BENCHMARK
178
179#endif // PULP_GPU_RUNTIME
std::chrono::steady_clock Clock
Definition event_loop.hpp:15
Definition gpu_fwd.hpp:36
SnapshotT< ReferenceSequencerConfig > Snapshot
Definition sequencer_state_channel.hpp:435
@ s
Definition input_events.hpp:41