Nugget
Bare-metal libraries and examples for the original PlayStation
Loading...
Searching...
No Matches
Classes | Static Public Member Functions | Friends | List of all members
psyqo::Profiler Class Reference

Scoped profiler: timed scopes recorded into a ring buffer. More...

#include <psyqo/profiler.hh>

Classes

struct  BenchResult
 
struct  Buffer
 Storage for the ring buffer, with room for Capacity scopes. More...
 
class  Scope
 Times its own lifetime. Use PSYQO_PROFILE_SCOPE rather than declaring one directly. More...
 
struct  Zone
 Static descriptor of one profiled scope. Its address is its identity. More...
 

Static Public Member Functions

template<unsigned Capacity>
static void install (Buffer< Capacity > &buffer)
 Claims root counter 2 and starts recording into the buffer.
 
static void uninstall ()
 Stops recording. Root counter 2 is left running.
 
static bool installed ()
 
static uint32_t now ()
 Reads a timestamp, as stored in the buffer.
 
static uint32_t elapsed (uint32_t start, uint32_t end)
 System clock cycles from timestamp start to timestamp end.
 
template<typename F >
static BenchResult bench (const Zone &zone, unsigned count, F &&function)
 Runs function count times, timing each run.
 
template<typename F >
static void forEach (F &&function)
 Calls function(zone, start, cycles, vsync, depth, stackDepth) for every entry still in the buffer, oldest first.
 
static bool dumpPCDRV (const char *path)
 Writes every entry still in the buffer to a host file through PCDRV, one line each, oldest first: vsync, depth, stack depth, start timestamp in hex, cycles, zone name, file:line. Returns false if the file could not be created or a write fell short.
 

Friends

class Scope
 

Detailed Description

Scoped profiler: timed scopes recorded into a ring buffer.

A PSYQO_PROFILE_SCOPE("name") statement times the rest of its enclosing block and records the zone, start, end, vsync count and nesting depth into a ring buffer when the block exits. Nothing is linked unless the program uses it, and nothing is recorded until a buffer is installed:

// once, after the GPU is initialized:
// anywhere:
void Renderer::render() {
...
}
static void install(Buffer< Capacity > &buffer)
Claims root counter 2 and starts recording into the buffer.
Definition profiler.hh:108
#define PSYQO_PROFILE_SCOPE(name)
Profiles the rest of the enclosing block under name, a string literal.
Definition profiler.hh:267
Storage for the ring buffer, with room for Capacity scopes.
Definition profiler.hh:92

PCSX-Redux reads the buffer live and draws it as a timeline in its Scope Profiler window. On a console, forEach or dumpPCDRV gets the entries out.

Time comes from root counter 2, which install claims and sets to free-run on the system clock, combined with psyqo's hblank counter on root counter 1. Every time is in system clock cycles, exact for scopes up to about 4 seconds long. The layout of the buffer, and how the two counters combine, are in common/hardware/pcsxprof.h.

Scopes are meant for the main loop. The nesting depth is one global, so a scope inside an interrupt handler, or one held across a coroutine suspension while another scope runs, records the wrong depth. The times stay right. A scope costs about 160 cycles in PCSX-Redux's interpreter (about 60 to begin, 100 to end), almost all outside its own measured span but inside the span of any scope around it. So a short function reads about that much lower than a per-function profile of the same build, which also counts the scope itself.

Member Function Documentation

◆ bench()

template<typename F >
static BenchResult psyqo::Profiler::bench ( const Zone &  zone,
unsigned  count,
F &&  function 
)
inlinestatic

Runs function count times, timing each run.

Each run is recorded under zone like a scope, and the result holds the run times in cycles. The cost of reading the counters twice is subtracted. Needs an installed buffer: without one, nothing runs and the result is empty. total wraps past 2^32 cycles, a bit over two minutes.

◆ dumpPCDRV()

bool psyqo::Profiler::dumpPCDRV ( const char *  path)
static

Writes every entry still in the buffer to a host file through PCDRV, one line each, oldest first: vsync, depth, stack depth, start timestamp in hex, cycles, zone name, file:line. Returns false if the file could not be created or a write fell short.

◆ elapsed()

static uint32_t psyqo::Profiler::elapsed ( uint32_t  start,
uint32_t  end 
)
inlinestatic

System clock cycles from timestamp start to timestamp end.

◆ forEach()

template<typename F >
static void psyqo::Profiler::forEach ( F &&  function)
inlinestatic

Calls function(zone, start, cycles, vsync, depth, stackDepth) for every entry still in the buffer, oldest first.

start is the raw timestamp, cycles the scope's duration. Do not record scopes from function.

◆ install()

template<unsigned Capacity>
static void psyqo::Profiler::install ( Buffer< Capacity > &  buffer)
inlinestatic

Claims root counter 2 and starts recording into the buffer.

Call it after the GPU is initialized, since the elapsed times depend on the hblank counter it sets up. It spends about 260 lines (one frame) measuring how many cycles a line takes. Under PCSX-Redux, the buffer is also registered so the emulator can read it. Installing another buffer replaces the first one.

◆ installed()

static bool psyqo::Profiler::installed ( )
inlinestatic

◆ now()

static uint32_t psyqo::Profiler::now ( )
inlinestatic

Reads a timestamp, as stored in the buffer.

◆ uninstall()

void psyqo::Profiler::uninstall ( )
static

Stops recording. Root counter 2 is left running.

Friends And Related Symbol Documentation

◆ Scope

friend class Scope
friend

The documentation for this class was generated from the following files: