NProfiler offers two profiling modes that collect performance data in fundamentally different ways: tracing and sampling.
Tracing involves inserting additional code into the profiled methods to measure times and hit counts. This is called instrumentation and is done automatically by the profiler before JIT compilation.
The code is inserted at the entry and exit points of methods and between individual lines of code or statements, where it performs measurements. The data is aggregated live and then transferred to the profiler UI.
The advantage of tracing is that it provides hit counts and information about various .NET events such as module loading, JIT compilation, etc., which are not available during sampling.
However, the injected code slows down the execution of the profiled methods, as a large number of additional injected lines of code must be executed for each profiled line of code. This profiler overhead is carefully estimated and subtracted by NProfiler's overhead compensation, as otherwise you would get incorrect and misleading profiling results.
Tracing is our preferred and recommended profiling mode.
Sampling works completely differently from tracing. With sampling, each thread is paused several hundred times per second to take a stack snapshot at random points in time. This is the same stack that you can view in the debugger.
Each stack snapshot is a sample (hence sampling) and indicates which methods and lines of code are currently being executed. If, in 1,000 stack snapshots, method A was on the stack 400 times and method B was on the stack 600 times, we assume that method A consumed 40% of the time and method B consumed 60% of the time.
The advantage of sampling is that profiled methods are not slowed down by injected code. The suspension of threads for stack snapshots is negligible. Unfortunately, however, this means that hit counts or event data such as module loading, JIT compilation, etc. cannot be collected.