Adds a tiny zone-based profiler (prof.{h,cpp}) enabled via KOBO_PROF=1.
Instruments run_game with zones for myship.move, enemies.move (split into
realize and move_pass), hit_structure, and hit_bolt. Adds KOBO_STRESS=N
to keep the enemy pool topped up with rocks for stable per-frame timing.
Why: the entity/render pipeline refactor in BENCH.md needs a comparable
baseline before any layout changes. Without a stress workload the natural
gameplay enemy count is too noisy to compare across commits.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
95 lines
2.5 KiB
C
95 lines
2.5 KiB
C
// Lightweight zone-based profiler for measuring the DOD refactor.
|
|
// Enabled at runtime via KOBO_PROF=1 in the environment.
|
|
// See plans: .claude/plans/can-you-explain-the-noble-wilkes.md (Phase 0).
|
|
|
|
#ifndef KOBO_PROF_H
|
|
#define KOBO_PROF_H
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#define PROF_MAX_ZONES 32
|
|
#define PROF_DUMP_EVERY_FRAMES 600
|
|
|
|
typedef struct {
|
|
const char *name;
|
|
uint64_t calls;
|
|
uint64_t total_ns;
|
|
} prof_zone_t;
|
|
|
|
extern prof_zone_t prof_zones[PROF_MAX_ZONES];
|
|
extern int prof_nzones;
|
|
extern int prof_enabled;
|
|
extern uint64_t prof_frame_count;
|
|
|
|
static inline uint64_t prof_now_ns(void)
|
|
{
|
|
#if defined(__APPLE__) || defined(__linux__)
|
|
struct timespec ts;
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
return (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
// Returns the index of the zone with the given name, creating it on first use.
|
|
// Names must be string literals (we compare by pointer for speed).
|
|
static inline int prof_zone_id(const char *name)
|
|
{
|
|
for (int i = 0; i < prof_nzones; ++i)
|
|
if (prof_zones[i].name == name)
|
|
return i;
|
|
if (prof_nzones >= PROF_MAX_ZONES)
|
|
return PROF_MAX_ZONES - 1;
|
|
int id = prof_nzones++;
|
|
prof_zones[id].name = name;
|
|
prof_zones[id].calls = 0;
|
|
prof_zones[id].total_ns = 0;
|
|
return id;
|
|
}
|
|
|
|
static inline void prof_accumulate(int id, uint64_t ns)
|
|
{
|
|
prof_zones[id].calls += 1;
|
|
prof_zones[id].total_ns += ns;
|
|
}
|
|
|
|
void prof_init(void);
|
|
void prof_frame_tick(void);
|
|
void prof_dump(FILE *f);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
|
|
// RAII helper for C++ scoping.
|
|
struct ProfScope {
|
|
int id;
|
|
uint64_t t0;
|
|
inline ProfScope(int zone_id) : id(zone_id), t0(prof_enabled ? prof_now_ns() : 0) {}
|
|
inline ~ProfScope() {
|
|
if (prof_enabled) prof_accumulate(id, prof_now_ns() - t0);
|
|
}
|
|
};
|
|
|
|
// Pastes the line number to make the static unique per use-site.
|
|
#define PROF_ZONE_CONCAT2(a,b) a##b
|
|
#define PROF_ZONE_CONCAT(a,b) PROF_ZONE_CONCAT2(a,b)
|
|
#define PROF_ZONE(name) \
|
|
static int PROF_ZONE_CONCAT(_prof_zid_, __LINE__) = -1; \
|
|
if (PROF_ZONE_CONCAT(_prof_zid_, __LINE__) < 0) \
|
|
PROF_ZONE_CONCAT(_prof_zid_, __LINE__) = prof_zone_id(name); \
|
|
ProfScope PROF_ZONE_CONCAT(_prof_scope_, __LINE__)( \
|
|
PROF_ZONE_CONCAT(_prof_zid_, __LINE__))
|
|
|
|
#endif
|
|
|
|
#endif // KOBO_PROF_H
|