profile

A function-level profiler for Jda programs. Measures execution time and call counts using clock_gettime(CLOCK_MONOTONIC). Supports up to 64 profiling slots with enter/leave instrumentation and a flat profile report.

Usage

import profile

fn main() {
    prof_init()
    prof_enable()

    // Profile a hot function
    prof_enter(0, "sort")
    sort_vec(v)
    prof_leave(0)

    prof_enter(1, "search")
    binary_search(v, 42)
    prof_leave(1)

    // Print flat profile report
    prof_report()
    // Output:
    //   === Profile Report ===
    //   sort           1 calls    1234 us    1234 us/call
    //   search         1 calls      12 us      12 us/call
    //   === End Report ===

    ret 0
}

Function Reference

FunctionSignatureDescription
prof_init()Initialise profiler state
prof_enable()Enable profiling
prof_disable()Disable profiling
prof_enabled() -> i64Check if profiling is enabled
prof_enter(slot: i64, name: &i8)Start timing a slot
prof_leave(slot: i64)Stop timing a slot
prof_calls(slot: i64) -> i64Return call count for a slot
prof_total_ns(slot: i64) -> i64Return total nanoseconds for a slot
prof_avg_ns(slot: i64) -> i64Return average nanoseconds per call
prof_name(slot: i64) -> &i8Return name of a slot
prof_slot_count() -> i64Return number of used slots
prof_reset(slot: i64)Reset a single slot
prof_reset_all()Reset all slots
prof_report()Print flat profile report

Detailed API

prof_init

fn prof_init()

Initialise the profiler. Resets all 64 slots and enables profiling. Call once at program start.

prof_enable

fn prof_enable()

Enable profiling. When disabled, prof_enter and prof_leave calls are no-ops for minimal overhead.

prof_disable

fn prof_disable()

Disable profiling. Timing calls become no-ops. Useful for disabling profiling in production while keeping instrumentation in the code.

prof_enter

fn prof_enter(slot: i64, name: &i8)

Start timing slot slot (0-63). Records the current timestamp and associates name with the slot. If profiling is disabled, this is a no-op.

prof_enter(0, "parse_json")
let doc = json_parse(input)
prof_leave(0)

Note: Slots can be reused across different call sites with the same name. The profiler accumulates time and call counts per slot.

prof_leave

fn prof_leave(slot: i64)

Stop timing slot slot. Computes elapsed time since the matching prof_enter and adds it to the slot’s total. Increments the call count.

Important: Always pair prof_leave with prof_enter. Calling prof_leave on a slot that wasn’t entered is undefined.

prof_calls

fn prof_calls(slot: i64) -> i64

Return the number of times prof_leave was called on this slot.

prof_enter(0, "process")
process_item(a)
prof_leave(0)
prof_enter(0, "process")
process_item(b)
prof_leave(0)
let n = prof_calls(0)  // n == 2

prof_total_ns

fn prof_total_ns(slot: i64) -> i64

Return the total accumulated time in nanoseconds for this slot across all enter/leave pairs.

prof_avg_ns

fn prof_avg_ns(slot: i64) -> i64

Return the average time per call in nanoseconds: total_ns / calls. Returns 0 if the slot has no calls.

prof_report

fn prof_report()

Print a flat profile report to stdout. Lists all used slots with name, call count, total microseconds, and average microseconds per call.

=== Profile Report ===
parse          1000 calls    45231 us      45 us/call
transform       500 calls    12044 us      24 us/call
emit           1000 calls     8921 us       8 us/call
=== End Report ===

Only slots that have been entered at least once are printed.

Example: Profiling a Pipeline

import profile

fn process_pipeline(data: &i8) {
    prof_enter(0, "parse")
    let parsed = parse(data)
    prof_leave(0)

    prof_enter(1, "validate")
    validate(parsed)
    prof_leave(1)

    prof_enter(2, "transform")
    let result = transform(parsed)
    prof_leave(2)

    prof_enter(3, "serialize")
    serialize(result)
    prof_leave(3)
}

fn main() {
    prof_init()
    prof_enable()

    for i in range(1000) {
        process_pipeline(input)
    }

    prof_report()
    ret 0
}