Using Profile.@profile manually

The @perfetto macro is the easy path: it runs your code, picks a good sampling rate, and renders the chart in one step. But sometimes you need more control:

  • Your workload has side effects and shouldn't be executed multiple times by the auto-calibration loop.
  • You want to profile a long-running session and visualize whatever samples have accumulated so far.
  • You're collecting samples in one process (e.g. a server) and want to view them somewhere else.
  • You want to use Profile's own knobs — Profile.init, Profile.@profile, @profile sample_rate=…, etc. — directly.

In all of these cases, collect samples with the Profile stdlib yourself, then hand them to perfetto_view (for in-notebook display) or perfetto_open (for the browser).

The basic recipe

using Profile, ProfilePerfetto

Profile.clear()
Profile.@profile my_workload()        # runs exactly once

perfetto_view()

That's it. With no arguments, perfetto_view (and perfetto_open) read whatever samples are currently sitting in Julia's profile buffer — the same buffer that Profile.@profile just wrote to.

# In a notebook (Pluto / VS Code / Jupyter):
perfetto_view()

# In the plain REPL, or whenever you want a full browser tab:
perfetto_open()

Tuning the sample rate

Auto-calibration is what @perfetto does for you. When you drive Profile yourself, you pick the rate. The default is one sample every 1 ms, which is often too coarse for fast workloads and too fine for long ones.

using Profile, ProfilePerfetto

# Sample every 100 µs — good for workloads in the millisecond range.
Profile.init(; delay = 0.0001)

Profile.clear()
Profile.@profile my_workload()

perfetto_view(; name = "my_workload, 100µs sampling")

Rule of thumb: aim for a few hundred to a few thousand samples per run. Fewer than ~50 and the chart is too sparse to read; more than ~100,000 and the buffer fills up and Perfetto starts to chug.

See Tuning the profiler for more on choosing a rate.

Passing data explicitly

If you've already pulled samples out of the profile buffer — for example to save them to disk, or to ship them between processes — pass them in directly:

data   = Profile.fetch(; include_meta = true)
lidict = Profile.getdict(data)

perfetto_view(data, lidict; name = "Today's run")
Always use `include_meta = true`

ProfilePerfetto needs the per-sample metadata (thread id, task id, timestamps) that Julia's Profile.fetch only emits when include_meta = true. Without it, the parser can't reconstruct the timeline.

Profiling a long-running session

You don't have to stop sampling before viewing. Call perfetto_view() whenever you want a snapshot — the profile buffer keeps accumulating in the background.

Profile.init(; delay = 0.001, n = 10_000_000)   # bigger buffer
Profile.clear()
Profile.@profile run_server()        # runs for a while…

# Later, in another cell or after Ctrl-C:
perfetto_view()

See also