# Valgrind Performance **Valgrind is slow:** 10-50x slowdown is typical, sometimes worse. This is necessary to observe every instruction and memory access, but it limits what you can profile in practice. Strategic choices about what and how to run Valgrind can make it manageable. **Choosing what to test:** Profile small test cases, not full production runs. Valgrind is best used in a test suite: ```bash # Run your test suite under Valgrind for test in ./tests/*; do valgrind --leak-check=full ./$test done ``` This catches bugs in correctness tests (small, focused) without trying to profile massive simulations. For HPC codes, extract a single small problem size or a single MPI rank and test that locally: ```bash # Profile only rank 0, with reduced problem size mpirun -n 1 valgrind --tool=memcheck ./program --size 100 ``` Avoid profiling full production runs—it will run for days. **Tool selection:** Different tools have different overhead: ``` memcheck 10-30x slowdown (most common) memcheck + track-origins 20-50x slowdown (useful but expensive) helgrind/DRD 5-20x slowdown (multithreading) callgrind 10-30x slowdown (instruction counting) cachegrind 5-20x slowdown (cache simulation) massif 5-15x slowdown (memory profiling) ``` memcheck is the slowest because it tracks every byte's initialization state. helgrind on a single-threaded program is misleading (many false negatives). Match the tool to your needs. **Optimizing Valgrind runs:** ```bash valgrind --trace-children=no ./program # don't profile child processes valgrind --main-stacksize=4000000 ./program # increase stack if needed valgrind -q --leak-check=summary ./program # less output, faster ``` `--trace-children=no` skips profiling spawned processes (sometimes huge overhead in complex programs). `-q` reduces output verbosity (less I/O overhead). `--leak-check=summary` (instead of `full`) gives totals without per-block details (faster). **Suppressing library overhead:** If most overhead comes from library code you can't optimize, use `--trace-ignore-regex` to skip instrumenting specific libraries: ```bash valgrind --trace-ignore-regex='.*libfoo.*' ./program ``` This reduces overhead but may miss bugs in that library. **Memory overhead:** Valgrind itself uses extra memory (typically 2-3x). For large programs, this alone can cause swapping. Run on a machine with ample RAM, or test smaller problem sizes. **Parallel Valgrind runs:** Profile different ranks or test cases in parallel on a multi-core machine: ```bash for i in {0..7}; do valgrind ./test_$i > valgrind_$i.log 2>&1 & done wait ``` This profiles 8 programs in parallel, reducing wall-clock time. **When not to use Valgrind:** If your code is already correct (all tests pass, no memory issues), Valgrind adds little value. Use [[perf]] for performance profiling on production. Use Valgrind during development for correctness checking, especially after refactoring or adding features. **Pragmatic approach:** Run Valgrind on your test suite in CI (continuous integration). Catch bugs early, keep tests focused and small, and address errors as they're discovered. This prevents technical debt from accumulating.