Site Tools


wiki:valgrind-performance

Table of Contents

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:

# 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:

# 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:

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:

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:

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.

wiki/valgrind-performance.md · Last modified: by 127.0.0.1