Skip to content
Plugins (183)

profiler

stable

A stateful code profiler that times named sections with nanosecond precision, recording call counts, totals, averages, and memory snapshots.

use plugin profiler::{Profiler, begin_section, end_section, …}
15 functions Utilities
Runs with the native Zolo host

Plugin snippets can be edited in the Playground, but native capabilities are executed locally with the Zolo CLI.

/ filter jk navigate Esc clear
Functions (15)
  1. Profiler Creates a new profiler instance.
  2. begin_section Start timing a named section
  3. end_section Stop timing a section, return elapsed ns
  4. report Get timing stats for all sections
  5. reset Clear all recorded timings
  6. active_sections List currently active section names
  7. section_count Count of distinct recorded sections
  8. section_stats Get stats for a single named section
  9. elapsed_ns Nanoseconds elapsed in an active section
  10. push_section Begin a nested (hierarchical) section
  11. pop_section End innermost nested section
  12. record_memory Record a manual memory snapshot
  13. memory_report Get all recorded memory snapshots
  14. format_report Format a report table as text
  15. format_flamegraph_lines Format report as folded stack lines

From import to result

  1. 01Import only what you use
  2. 02Copy the smallest example
  3. 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)

Global index

Find your way through Zolo

Try an idea

Start here

9 results

9 results

en