What Is Sampling in Profiling and How Does a Sampling Profiler Find Performance Bottlenecks?

Sampling in profiling means periodically capturing the call stack of a running program instead of instrumenting every function entry and exit. A sampling profiler interrupts the process at short intervals, records which function is executing at that instant, and aggregates those snapshots into a statistical picture of where time is spent. The result is a low-overhead estimate of hot spots that requires no source changes — at the cost of being approximate rather than exact.

How sampling differs from instrumenting profilers

An instrumenting profiler rewrites or hooks each function so it can count calls and measure durations precisely. That precision has a price: added overhead per call, distorted timing for very short functions, and often a need to rebuild the code with special flags.

A sampling profiler takes the opposite approach:

  • Input: a running process (or a launched executable) plus a sampling interval, typically in the low milliseconds.
  • Action: at each tick, the profiler walks the current call stack and records the frames.
  • Output: counts and percentages per function, per module, or per call path.

Because it observes rather than modifies, sampling usually adds only a small, roughly constant overhead, and it can profile release builds that match what users actually run.

Why the numbers are statistical

Each sample is a snapshot. If a function appears in 30% of samples, the estimate is that roughly 30% of execution time was spent there. That estimate gets more reliable as the sample count grows, which is why longer runs and shorter intervals produce steadier results.

Two consequences follow:

  • Short or rarely called routines can be missed if they never happen to be on the stack when a tick fires. Increase the run duration or lower the interval to catch them.
  • Inlined code blurs attribution. If the compiler inlined a small function into its caller, samples land on the caller, not the original function. The same applies to code with no distinct stack frame.

Treat the output as a ranking of where time goes, not as an exact accounting.

Reading a sampling profiler's output

Most sampling profilers present the same core views:

View What it shows How to use it
Flat / function list Self time per function Find the top entries — these are your first candidates
Call tree / callers-callees Which paths lead to a hot function Decide whether the cost is intrinsic or caused by a caller
Module breakdown Time per DLL/unit Separate your code from framework or library cost

The workflow is straightforward:

  1. Run the target under the profiler and exercise the slow scenario.
  2. Sort by self time and look at the top few functions.
  3. For each, check the call tree to see who calls it and how often.
  4. Change one thing, re-profile, and compare — the ranking should shift if the fix worked.

A function high in self time is doing work directly; a function high in total (inclusive) time but low in self time is mostly waiting on its callees, so look further down the tree.

A concrete tool: SamplingProfiler for Delphi/Pascal

DelphiTools.info is the home of SamplingProfiler, a free code profiler for Delphi, alongside DWScript and other Pascal developer tools. For Delphi and Pascal work it is the practical example of the workflow above: attach to or launch a build, collect samples while the application runs, then inspect the aggregated function and call-path data to locate bottlenecks. The same site publishes optimization and coding tips, which pair naturally with profiling results — you profile to find the hot function, then apply targeted optimization rather than guessing.

Trade-offs to keep in mind

  • Low overhead, approximate results. Good for finding the dominant cost; not a substitute for exact call counts.
  • Blind spots for tiny or inlined routines. Mitigate with longer runs, finer intervals, or targeted instrumentation if you need certainty.
  • Needs a representative workload. Samples only reflect what you actually execute, so profile the scenario you care about.
  • Debug vs. release builds. Sampling can profile optimized builds, which is usually what you want, but symbol information must be available to map addresses back to function names.

If you need exact per-call timing for a handful of functions, an instrumenting profiler is the better fit. If you need to know, quickly and cheaply, where a large Delphi or Pascal application spends its time, sampling is the right starting point.

delphitools.info
Home of SamplingProfiler, a free code profiler for Delphi, DWScript (Delphi Web Script) and other Pascal developer tools, optimization and coding tip…