Profiling, Benchmarking, and Sanitizers
Error Handling, Tooling, and Multi-File Programs

8.9 Profiling, Benchmarking, and Sanitizers

Hardware informed developers don't guess if their code is fast or memory safe; they measure it. Because C++ gives you raw access to RAM, you are responsible for avoiding memory leaks and out of bounds array accesses.

Let's look at compiler sanitizers for debugging, and micro benchmarking for performance.

Compiler Sanitizers (The Safety Net)

Sanitizers are powerful instrumentation tools built directly into modern GCC and Clang compilers. When enabled, they inject extra checking code into your binary during compilation. They will violently crash your program the absolute millisecond you violate memory rules, printing exactly which line of code caused the issue.

To enable them, you pass flags during compilation or add them to your CMake targets:

* AddressSanitizer (ASan) -fsanitize=address: Detects memory leaks, use after free (dangling pointers), and array out of bounds accesses.

* ThreadSanitizer (TSan) -fsanitize=thread: Detects many data races and some synchronization mistakes. It is not a proof that a concurrent design is deadlock-free.

* UndefinedBehaviorSanitizer (UBSan) -fsanitize=undefined: Detects many forms of undefined behavior, including signed integer overflow and invalid shifts. Enable the checks you need; it does not diagnose every bug.

Compile with: g++ main.cpp -fsanitize=address -g
#include <iostream>

int main() {
    int* heapArray = new int[10];
    heapArray[10] = 5; // CRASH! ASan instantly detects the out-of-bounds write.
    
    // Forgot to call delete[] heapArray! ASan will print a memory leak report on exit.
    return 0;
}
Enabling AddressSanitizer to catch memory leaks.

Sanitizers can add substantial overhead, but the amount varies by sanitizer and workload. Run them in dedicated test or CI configurations; production use is a deliberate operational trade-off, not a universal prohibition.

Micro benchmarking with Google Benchmark

If you want to prove that your Struct of Arrays layout is faster than an Array of Structs layout, using std::chrono to measure execution time is often inaccurate due to compiler optimizations and OS background noise.

The industry standard is the Google Benchmark library. It runs your function thousands of times, warming up the CPU cache, preventing the compiler from optimizing away your dead code loops, and printing statistically accurate nanosecond execution times.

#include <benchmark/benchmark.h>
#include <vector>

static void BM_VectorCreation(benchmark::State& state) {
    // The loop runs repeatedly until the library gathers enough statistical data
    for (auto _ : state) {
        std::vector<int> v(1000, 1);
        // Prevents the compiler from deleting 'v' as dead code
        benchmark::DoNotOptimize(v.data());
    }
}
// Register the function as a benchmark
BENCHMARK(BM_VectorCreation);

BENCHMARK_MAIN();
A basic Google Benchmark test structure.

* The Final Benchmark: Systems engineering is empirical. Benchmark a release-like configuration appropriate for the target (often -O2 or -O3), record compiler and hardware details, and use profilers such as perf where available. Measurements answer specific questions; interpret them with the workload and methodology attached.

Finished reading this lesson?