Continuous profiling produces a full stack sample every 10ms per core. Storing each one independently would be absurd — Helix stores deltas against a shared call-tree instead.

Continuous profiling adds a fourth signal alongside metrics, logs, and traces: periodic stack samples, typically every 10ms per core under CPU profiling. At that rate, a single 16-core host produces 1,600 stack samples a second. Storing each as an independent list of frames would be enormous and mostly redundant, since most samples share the bulk of their call tree with the sample before them.

The shared call-tree structure

Instead of storing each sample as a flat frame list, Helix maintains a shared call-tree per profiled process, where each unique function-call path is a node visited once, and a sample is stored as a reference to the deepest node it reached plus a hit-count increment along that path. Two samples that share the same call path up to frame 12 and diverge after that only cost storage for the divergent tail.

from profiles
| where service == 'checkout-api' and type == 'cpu'
| where range == 1h
| summarize sum(self_time) by function
| where self_time > 5%
| top 10 by self_time desc

Measured storage impact

On a representative profiling session — 16 cores, 10ms sampling, one hour, roughly 5.76 million individual stack samples — a naive flat-frame-list encoding would cost approximately 890MB uncompressed (averaging ~155 bytes per sample across typical stack depths of 20-40 frames). The shared call-tree delta encoding brought that down to about 34MB for the same session, a 96% reduction, because the overwhelming majority of samples in a real workload retrace a small number of hot paths.

Merging profiles across pods for a service view

A service-level flame graph query has to merge call trees from potentially hundreds of pod-level profiling sessions. Because each pod's call tree uses semantically identical function names (from the same binary or interpreter), merging is a tree-union operation keyed by function symbol rather than a raw byte-level merge — on a 200-pod service, this merge for a 1-hour window completes in about 380ms.

What this means for adoption cost

Because storage cost per sampled second is so much lower than naive encoding would suggest, we default continuous profiling to always-on at 10ms resolution rather than the opt-in, sampled-window approach some other profilers use — a design decision that only holds up economically because of the delta encoding described here.

  • Stack samples are stored as references into a shared call-tree, not independent frame lists.
  • Measured storage reduction on a representative session: 96% (890MB to 34MB) versus naive flat encoding.
  • Service-level flame graph merges across 200 pods complete in about 380ms via symbol-keyed tree union.