OpenSWMM Engine  6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
KokkosPerfCounters.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2//
3// Copyright 2026 Caleb Buahin
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
53
54#ifndef OPENSWMM_ENGINE_2D_GPU_KOKKOS_PERF_COUNTERS_HPP
55#define OPENSWMM_ENGINE_2D_GPU_KOKKOS_PERF_COUNTERS_HPP
56
57#include <Kokkos_Core.hpp>
58
59#include <algorithm>
60#include <chrono>
61#include <cstdint>
62#include <cstdio>
63#include <cstdlib>
64#include <cstring>
65#include <map>
66#include <string>
67#include <vector>
68
70
72
74 std::string label;
75 long n = 0;
76 double sec = 0.0;
77};
78
79struct Counters {
80 long n_advance = 0;
81 long n_launch = 0;
82 long n_fence = 0;
83 double sec_fence = 0.0;
84 long n_deep_copy = 0;
85 unsigned long long bytes_h2d = 0, bytes_d2h = 0, bytes_same = 0;
86 std::vector<KernelEntry> kernels;
87 std::map<std::string, std::uint64_t> label_id;
88};
89
90// Each solver owns its counters. Only dispatches in its active call scope
91// are attributed to it; calls from unrelated Kokkos clients are ignored.
92inline thread_local Counters* active = nullptr;
93class Scope {
94public:
103 explicit Scope(Counters& counters) : previous_(active) { active = &counters; }
109 ~Scope() { active = previous_; }
110 Scope(const Scope&) = delete;
111
112private:
113 Counters* previous_;
114};
115
116namespace detail {
117
118using clock = std::chrono::steady_clock;
119
123inline thread_local std::vector<clock::time_point> dispatch_t0;
124inline thread_local std::vector<clock::time_point> fence_t0;
125
139inline void begin_dispatch(const char* name, std::uint32_t /*devid*/, std::uint64_t* kID) {
140 *kID = 0;
141 if (!active)
142 return;
143 auto& g = *active;
144 const auto it = g.label_id.find(name);
145 std::uint64_t id;
146 if (it == g.label_id.end()) {
147 id = g.kernels.size();
148 g.label_id.emplace(name, id);
149 g.kernels.push_back({name, 0, 0.0});
150 } else {
151 id = it->second;
152 }
153 *kID = id;
154 ++g.n_launch;
155 dispatch_t0.push_back(clock::now());
156}
157
165inline void end_dispatch(std::uint64_t kID) {
166 if (!active)
167 return;
168 auto& g = *active;
169 if (dispatch_t0.empty())
170 return;
171 const double dt = std::chrono::duration<double>(clock::now() - dispatch_t0.back()).count();
172 dispatch_t0.pop_back();
173 if (kID < g.kernels.size()) {
174 ++g.kernels[kID].n;
175 g.kernels[kID].sec += dt;
176 }
177}
178
187inline void begin_fence(const char* /*name*/, std::uint32_t /*devid*/, std::uint64_t* handle) {
188 *handle = 0;
189 if (!active)
190 return;
191 fence_t0.push_back(clock::now());
192}
193
202inline void end_fence(std::uint64_t /*handle*/) {
203 if (!active)
204 return;
205 auto& g = *active;
206 if (fence_t0.empty())
207 return;
208 g.sec_fence += std::chrono::duration<double>(clock::now() - fence_t0.back()).count();
209 fence_t0.pop_back();
210 ++g.n_fence;
211}
212
224inline void begin_deep_copy(Kokkos_Profiling_SpaceHandle dst, const char*, const void*,
225 Kokkos_Profiling_SpaceHandle src, const char*, const void*,
226 std::uint64_t size) {
227 if (!active)
228 return;
229 auto& g = *active;
230 ++g.n_deep_copy;
231 const bool dst_host = std::strncmp(dst.name, "Host", 4) == 0;
232 const bool src_host = std::strncmp(src.name, "Host", 4) == 0;
233 if (src_host && !dst_host)
234 g.bytes_h2d += size;
235 else if (dst_host && !src_host)
236 g.bytes_d2h += size;
237 else
238 g.bytes_same += size; // OMP backend: all
239}
240
241} // namespace detail
242
252inline void install() {
253 static const bool done = [] {
254 if (!perf::enabled())
255 return false;
256 if (std::getenv("KOKKOS_TOOLS_LIBS") || std::getenv("KOKKOS_PROFILE_LIBRARY")) {
257 std::fprintf(stderr, "[PERF-2D-KOKKOS] external Kokkos tool loaded; in-process "
258 "counters disabled for this run.\n");
259 return false;
260 }
261 namespace KTE = Kokkos::Tools::Experimental;
262 KTE::set_begin_parallel_for_callback(detail::begin_dispatch);
263 KTE::set_end_parallel_for_callback(detail::end_dispatch);
264 KTE::set_begin_parallel_reduce_callback(detail::begin_dispatch);
265 KTE::set_end_parallel_reduce_callback(detail::end_dispatch);
266 KTE::set_begin_parallel_scan_callback(detail::begin_dispatch);
267 KTE::set_end_parallel_scan_callback(detail::end_dispatch);
268 KTE::set_begin_fence_callback(detail::begin_fence);
269 KTE::set_end_fence_callback(detail::end_fence);
270 KTE::set_begin_deep_copy_callback(detail::begin_deep_copy);
271 return true;
272 }();
273 (void)done;
274}
275
283inline void reset() {
284 if (!active)
285 return;
286 auto& g = *active;
287 g = Counters{};
288}
289
295inline void count_advance() {
296 if (!active)
297 return;
298 auto& g = *active;
299 if (perf::enabled())
300 ++g.n_advance;
301}
302
313inline void dump() {
314 if (!active)
315 return;
316 auto& g = *active;
317 if (!perf::enabled() || g.n_advance == 0)
318 return;
319 const double adv = static_cast<double>(g.n_advance);
320 std::fprintf(stderr,
321 "[PERF-2D-KOKKOS] advances=%ld launches=%ld launches_per_advance=%.1f "
322 "fences=%ld fence_s=%.4f deep_copies=%ld "
323 "h2d_bytes=%llu d2h_bytes=%llu samespace_bytes=%llu "
324 "h2d_bytes_per_advance=%.0f d2h_bytes_per_advance=%.0f\n",
325 g.n_advance, g.n_launch, g.n_launch / adv, g.n_fence, g.sec_fence, g.n_deep_copy,
326 g.bytes_h2d, g.bytes_d2h, g.bytes_same, g.bytes_h2d / adv, g.bytes_d2h / adv);
327 std::vector<const KernelEntry*> order;
328 order.reserve(g.kernels.size());
329 for (const auto& k : g.kernels)
330 order.push_back(&k);
331 std::sort(order.begin(), order.end(),
332 [](const KernelEntry* a, const KernelEntry* b) { return a->sec > b->sec; });
333 for (const KernelEntry* k : order)
334 std::fprintf(stderr, "[PERF-2D-KOKKOS-KERNEL] name=%s n=%ld s=%.4f per_advance=%.1f\n",
335 k->label.c_str(), k->n, k->sec, k->n / adv);
336}
337
338} // namespace openswmm::twoD::gpu::kperf
339
340#endif // OPENSWMM_ENGINE_2D_GPU_KOKKOS_PERF_COUNTERS_HPP
Performance timing and phase accumulators for engine execution.
~Scope()
Restore this thread's previous counter attribution on scope exit.
Definition KokkosPerfCounters.hpp:109
Scope(const Scope &)=delete
Scope(Counters &counters)
Attribute this thread's nested Kokkos dispatches to one solver counter set.
Definition KokkosPerfCounters.hpp:103
bool enabled() noexcept
Read the process-cached profiling environment gate.
Definition PerfTimers.hpp:238
Definition KokkosPerfCounters.hpp:116
std::chrono::steady_clock clock
Definition KokkosPerfCounters.hpp:118
void end_dispatch(std::uint64_t kID)
Finish a paired host-side dispatch timing record.
Definition KokkosPerfCounters.hpp:165
thread_local std::vector< clock::time_point > dispatch_t0
Definition KokkosPerfCounters.hpp:123
thread_local std::vector< clock::time_point > fence_t0
Definition KokkosPerfCounters.hpp:124
void end_fence(std::uint64_t)
Accumulate elapsed host wall for a paired Kokkos fence.
Definition KokkosPerfCounters.hpp:202
void begin_deep_copy(Kokkos_Profiling_SpaceHandle dst, const char *, const void *, Kokkos_Profiling_SpaceHandle src, const char *, const void *, std::uint64_t size)
Count a Kokkos deep-copy dispatch and its logical direction/bytes.
Definition KokkosPerfCounters.hpp:224
void begin_dispatch(const char *name, std::uint32_t, std::uint64_t *kID)
Begin one paired host-dispatch timing record.
Definition KokkosPerfCounters.hpp:139
void begin_fence(const char *, std::uint32_t, std::uint64_t *handle)
Start timing a Kokkos fence callback pair.
Definition KokkosPerfCounters.hpp:187
Definition KokkosPerfCounters.hpp:71
thread_local Counters * active
Definition KokkosPerfCounters.hpp:92
void dump()
One [PERF-2D-KOKKOS] summary line plus one line per kernel label, sorted by host-side dispatch wall d...
Definition KokkosPerfCounters.hpp:313
void reset()
Replace active solver counters with empty defaults.
Definition KokkosPerfCounters.hpp:283
void install()
Registers the counting callbacks once per process. No-op unless OPENSWMM_PERF is set; defers to an ex...
Definition KokkosPerfCounters.hpp:252
void count_advance()
Count one managed solver advance when profiling is enabled.
Definition KokkosPerfCounters.hpp:295
Definition KokkosPerfCounters.hpp:79
std::map< std::string, std::uint64_t > label_id
Definition KokkosPerfCounters.hpp:87
long n_advance
Definition KokkosPerfCounters.hpp:80
unsigned long long bytes_same
Definition KokkosPerfCounters.hpp:85
long n_deep_copy
Definition KokkosPerfCounters.hpp:84
long n_launch
parallel_for + reduce + scan
Definition KokkosPerfCounters.hpp:81
unsigned long long bytes_d2h
Definition KokkosPerfCounters.hpp:85
std::vector< KernelEntry > kernels
indexed by kernel id
Definition KokkosPerfCounters.hpp:86
double sec_fence
Definition KokkosPerfCounters.hpp:83
long n_fence
Definition KokkosPerfCounters.hpp:82
unsigned long long bytes_h2d
Definition KokkosPerfCounters.hpp:85
Definition KokkosPerfCounters.hpp:73
std::string label
Definition KokkosPerfCounters.hpp:74
double sec
host-side dispatch wall (== execution on OMP)
Definition KokkosPerfCounters.hpp:76
long n
launches
Definition KokkosPerfCounters.hpp:75