Tracy Profiler - Real-Time Frame Profiler Cheatsheet
Tracy is a real-time, nanosecond-resolution hybrid frame and sampling profiler, widely used in games and low-latency C++ and Rust. You add lightweight zone macros to your code, and Tracy streams timing data live to a desktop UI where you can see every frame, every zone, GPU timings, memory allocations, and lock contention as the program runs. Its combination of manual instrumentation plus automatic sampling gives both precise semantic zones and unattributed hot spots.
Installation
| Component | How |
|---|
| Server (UI) | Build profiler/ from the repo or download a release |
| Client (library) | Add TracyClient.cpp + headers to your build |
| Rust | cargo add tracy-client (or tracing-tracy) |
| Enable | Compile with TRACY_ENABLE defined |
| Verify | Launch the UI; it waits for a client connection |
Instrumenting C++
#include "tracy/Tracy.hpp"
void update() {
ZoneScoped; // zone named after the function
physics();
}
void render() {
ZoneScopedN("Render Pass"); // explicit name
ZoneColor(0x00FF00);
draw();
}
int main() {
while (running) {
update();
render();
FrameMark; // marks a frame boundary
}
}
| Macro | Purpose |
|---|
ZoneScoped | Time the enclosing scope |
ZoneScopedN("name") | Named zone |
ZoneText(str, len) | Attach text to a zone |
FrameMark | Delimit frames |
TracyPlot("fps", v) | Plot a numeric value over time |
TracyMessage(...) | Log a message into the timeline |
TracyLockable(...) | Track a mutex’s contention |
Instrumenting Rust
use tracy_client::{Client, span};
fn main() {
let _client = Client::start();
loop {
{
let _s = span!("update");
update();
}
tracy_client::frame_mark();
}
}
Or wire it into tracing with the tracing-tracy crate so existing spans appear in Tracy automatically.
Memory & GPU
| Feature | How |
|---|
| Allocation tracking | TracyAlloc(ptr, size) / TracyFree(ptr) |
| Custom allocators | Wrap alloc/free with the macros |
| GPU zones (Vulkan/D3D/OpenGL) | TracyVkZone, TracyD3D12Zone, etc. |
| GPU context | Create once, then zones attach to it |
The UI
| View | Shows |
|---|
| Timeline | Zones per thread, frame by frame |
| Statistics | Aggregate time per zone |
| Find zone | Histogram of one zone’s durations |
| Compare | Diff two captures (before/after a change) |
| Memory | Allocation timeline and leaks |
| Locks | Contention between threads |
| Flame graph | Aggregated call structure |
Workflow: Chasing a Frame Spike
| Step | Action |
|---|
| 1 | Run with the client connected; reproduce the hitch |
| 2 | Find the long frame in the timeline |
| 3 | Zoom in — which zone dominates? |
| 4 | Open “Find Zone” for that zone → duration histogram |
| 5 | Check the outlier’s context (locks, allocations, GPU wait) |
| 6 | Fix, capture again, use Compare to prove the improvement |
That compare-two-captures workflow is one of Tracy’s most practical features — it turns “feels faster” into a measured delta.
Sampling Mode
Beyond manual zones, Tracy can sample call stacks automatically (Linux/Windows), catching time in code you did not instrument. Combine both: zones give semantic structure, sampling fills the gaps.
| Aspect | Tracy | perf | Perfetto |
|---|
| Model | Instrumented zones + sampling | Sampling | System-wide tracing |
| Resolution | Nanosecond | Sample interval | Event-level |
| Live view | Yes (real-time) | No | Post-hoc |
| GPU support | Built-in | No | Partial |
| Best for | Games, frame-based, low-latency C++/Rust | CPU hot paths | Whole-system timelines |
Complements perf and Perfetto; Tracy’s edge is live, frame-oriented, instrumented profiling.
Resources