|
group2 0.1.0
CSE 125 Group 2
|
Lightweight server-side scoped profiler. More...
#include <SDL3/SDL.h>#include <array>#include <atomic>#include <cstdint>#include <functional>#include <limits>Go to the source code of this file.
Classes | |
| struct | group2::perf::PerScopeStats |
| Per-scope, all-thread atomic counters. More... | |
| struct | group2::perf::NetworkCounters |
| Per-tick network counters maintained by the network code. More... | |
| struct | group2::perf::Snapshot |
| Globally-visible snapshot returned to the aggregator callback. More... | |
| struct | group2::perf::Snapshot::ScopeSummary |
| Per-scope summary, filled for [0, scopeCount). More... | |
| class | group2::perf::ScopeTimer |
| RAII scoped timer. More... | |
Namespaces | |
| namespace | group2 |
| namespace | group2::perf |
Macros | |
| #define | GROUP2_PROF_SCOPE(label) |
| Drop-in scoped timer. | |
Typedefs | |
| using | group2::perf::ScopeId = std::uint16_t |
| Dense small id used to index the global stats table. | |
Functions | |
| ScopeId | group2::perf::registerScope (const char *name) |
| Register (or look up) a scope name and return its dense id. | |
| const char * | group2::perf::scopeName (ScopeId id) |
| Returns the human-readable name a ScopeId was registered with, or "" if id is out of range. | |
| std::size_t | group2::perf::scopeCount () |
| Returns the highest registered id + 1. | |
| void | group2::perf::initFromEnv () |
| Initialize from environment variables. | |
| void | group2::perf::startAggregator (std::function< void(const Snapshot &)> cb) |
| Spawn the 1 Hz aggregator thread. | |
| void | group2::perf::stopAggregator () |
| Stop the aggregator and join its thread. Idempotent. | |
| void | group2::perf::recordSample (ScopeId id, std::uint64_t ticks) noexcept |
| Recording entry point — public so unit tests can invoke it directly without a real ScopeTimer. | |
| void | group2::perf::tickEnd (std::uint64_t tickWallNs) noexcept |
| Tick boundary marker — call once per server tick() end. | |
| NetworkCounters & | group2::perf::net () |
| Network counter accessor. Hot-path code increments these directly. | |
| std::uint64_t | group2::perf::ticksToNs (std::uint64_t ticks) noexcept |
| Convenience: convert SDL performance-counter ticks to nanoseconds. | |
Variables | |
| constexpr std::size_t | group2::perf::k_maxScopes = 64 |
| Compile-time caps. | |
| constexpr std::size_t | group2::perf::k_histogramBuckets = 32 |
| Histogram bucket count. | |
| constexpr ScopeId | group2::perf::k_invalidScope = static_cast<ScopeId>(-1) |
Lightweight server-side scoped profiler.
Goal: measure where the per-tick budget goes at 150–500 connected bots without perturbing the measurement. Cost per scope is ~70 ns (two SDL_GetPerformanceCounter reads + a handful of relaxed atomic updates). At ~14 scopes per tick × 128 Hz that is ~125 µs/s of pure profiler overhead — a measurement noise floor we treat as the empty cost of having the system on at all.
Layered on top:
Toggling: GROUP2_SERVER_PROFILE=1 enables sample collection at startup. With the env var unset the macro still expands but every ScopeTimer ctor early-outs on the cached enabled.load(), so the cost collapses to one relaxed atomic load per scope (~1 ns).
Thread-safety: every counter in this header is std::atomic with relaxed ordering. We deliberately do not use a per-thread buffer flushed at aggregation time — the simpler all-atomic path is correct under any future parallel-system layout (PR-3+) and the extra atomic-ops cost is below the SDL clock-read cost we pay anyway.
| #define GROUP2_PROF_SCOPE | ( | label | ) |
Drop-in scoped timer.
The scope name is registered exactly once per call site (cached via a function-local static const).
Note: we use __LINE__ rather than __COUNTER__ because the latter is a compiler extension flagged by -Wpedantic / -Wc2y-extensions. This means two GROUP2_PROF_SCOPE calls on the same source line would collide; in practice every scope macro lives on its own line inside a brace block, so the collision can't happen.
Usage: