profiler
stableA stateful code profiler that times named sections with nanosecond precision, recording call counts, totals, averages, and memory snapshots.
use plugin profiler Plugin snippets can be edited in the Playground, but native capabilities are executed locally with the Zolo CLI.
Functions (15)
- Profiler Creates a new profiler instance.
- begin_section Start timing a named section
- end_section Stop timing a section, return elapsed ns
- report Get timing stats for all sections
- reset Clear all recorded timings
- active_sections List currently active section names
- section_count Count of distinct recorded sections
- section_stats Get stats for a single named section
- elapsed_ns Nanoseconds elapsed in an active section
- push_section Begin a nested (hierarchical) section
- pop_section End innermost nested section
- record_memory Record a manual memory snapshot
- memory_report Get all recorded memory snapshots
- format_report Format a report table as text
- format_flamegraph_lines Format report as folded stack lines
From import to result
- 01Import only what you use
- 02Copy the smallest example
- 03Run it with the Zolo CLI
Overview¶
profiler is a stateful, in-process code profiler built around a single handle
object. You create one with Profiler(), then bracket the code you care about
with begin_section / end_section pairs (or the stack-based
push_section / pop_section for nested call trees). Every measurement is
stored with nanosecond precision, so calling the same section repeatedly
accumulates call counts, totals, averages, and min/max — exactly what you need
to find a hot path across many iterations.
The mental model is: one profiler handle holds all your sections. Timings are
recorded internally as you end sections; report and section_stats read those
aggregates back as plain tables, memory_report reads manual memory snapshots,
and the standalone format_report / format_flamegraph_lines helpers turn a
report table into printable text or folded-stack lines for flamegraph tooling.
Reach for it whenever you want lightweight, dependency-free timing without
wiring up an external profiler.
Common patterns¶
Time a block, run it many times, then print the aggregated table:
use plugin profiler::{Profiler, format_report}
let p = Profiler()
for i in 0..1000 {
p.begin_section("hash")
// ... work to measure ...
p.end_section("hash")
}
print(format_report(p.report()))Build a nested call tree with the section stack and export it for a flamegraph:
use plugin profiler::{Profiler, format_flamegraph_lines}
let p = Profiler()
p.push_section("frame")
p.push_section("physics")
p.pop_section()
p.push_section("render")
p.pop_section()
p.pop_section()
print(format_flamegraph_lines(p.report()))Pair timing with manual memory snapshots to see cost and growth side by side:
use plugin profiler::{Profiler}
let p = Profiler()
p.begin_section("load")
p.record_memory("before", 1048576)
// ... load work ...
p.record_memory("after", 4194304)
p.end_section("load")
for _, snap in p.memory_report() {
print("{snap["label"]}: {snap["bytes"]} bytes")
}Creates a new profiler instance.
Creates a new profiler instance. Use begin_section / end_section pairs to measure code.
use plugin profiler::{Profiler, format_report}
let p = Profiler()
p.begin_section("load")
// ... work ...
p.end_section("load")
let report = p.report()
print(format_report(report))Start timing a named section
Starts a timer for a named section. Multiple sections can be active simultaneously.
p.begin_section("parse")
p.begin_section("validate")Stop timing a section, return elapsed ns
Stops the timer for the named section and returns #{elapsed_ns: int}. The elapsed time is also stored internally.
let result = p.end_section("parse")
print("parse took {result["elapsed_ns"]} ns")Calling the same section name repeatedly accumulates separate samples, so the
average and call count grow with each begin_section / end_section pair:
use plugin profiler::{Profiler}
let p = Profiler()
for i in 0..3 {
p.begin_section("step")
p.end_section("step")
}
print("recorded {p.section_count()} distinct section")Get timing stats for all sections
Returns a table of timing entries, one per recorded section, sorted alphabetically. Each entry has name, calls, total_ns, avg_ns, min_ns, max_ns.
let r = p.report()
for _, entry in r {
print("{entry["name"]}: avg {entry["avg_ns"]} ns over {entry["calls"]} calls")
}The report table feeds directly into either text formatter — pass it to
format_report for an aligned table or to format_flamegraph_lines for folded
stacks:
use plugin profiler::{Profiler, format_report}
let p = Profiler()
p.begin_section("a")
p.end_section("a")
p.begin_section("b")
p.end_section("b")
print(format_report(p.report()))Clear all recorded timings
Clears all section timings and active timers. Useful for resetting between benchmark iterations.
p.reset()List currently active section names
Returns a sorted list of section names that are currently being timed (started but not yet ended).
let active = p.active_sections()
print("still timing: {active[1]}")Count of distinct recorded sections
Returns the number of distinct sections that have been recorded (ended at least once).
print("measured {p.section_count()} sections")Get stats for a single named section
Returns timing stats for a single named section: name, calls, total_ns, avg_ns, min_ns, max_ns. Errors if the section has never been recorded.
let stats = p.section_stats("render")
print("render avg: {stats["avg_ns"]} ns")Use it to inspect the spread of a single hot section without rendering the whole report:
use plugin profiler::{Profiler}
let p = Profiler()
for i in 0..5 {
p.begin_section("query")
p.end_section("query")
}
let s = p.section_stats("query")
print("query: {s["calls"]} calls, min {s["min_ns"]} / max {s["max_ns"]} ns")Nanoseconds elapsed in an active section
Returns the nanoseconds elapsed so far for an active (not yet ended) section. Useful for progress logging inside long operations.
p.begin_section("import")
// ... some work ...
print("so far: {p.elapsed_ns("import")} ns")
p.end_section("import")Begin a nested (hierarchical) section
Begins a nested section. The full name becomes parent/child, building a hierarchical call tree compatible with flamegraph tools.
p.push_section("frame")
p.push_section("physics")
// ... physics work ...
p.pop_section()
p.pop_section()End innermost nested section
Ends the innermost pushed section and returns #{name: string, elapsed_ns: int}.
let r = p.pop_section()
print("finished {r["name"]} in {r["elapsed_ns"]} ns")Record a manual memory snapshot
Records a manual memory measurement snapshot with a text label and a byte count.
p.record_memory("after_load", 1048576)
p.record_memory("after_parse", 2097152)Get all recorded memory snapshots
Returns all memory snapshots in insertion order. Each entry has label and bytes.
let mem = p.memory_report()
for _, snap in mem {
print("{snap["label"]}: {snap["bytes"]} bytes")
}Format a report table as text
Formats a report table (from report()) as an aligned text table with columns: Section, Calls, Total(ns), Avg(ns), Min(ns), Max(ns).
use plugin profiler::{Profiler, format_report}
let p = Profiler()
p.begin_section("work")
p.end_section("work")
print(format_report(p.report()))Format report as folded stack lines
Converts a report table into folded stack format (name total_ns), one line per section. Feed the output to inferno or similar tools.
use plugin profiler::{Profiler, format_flamegraph_lines}
let p = Profiler()
p.push_section("app")
p.push_section("db")
p.pop_section()
p.pop_section()
let lines = format_flamegraph_lines(p.report())
print(lines)