valgrind y profilers (perf, cachegrind)
Los sanitizers instrumentan tu build. Valgrind y los profilers observan la ejecución desde
ángulos distintos. Memcheck corre tu programa sobre la CPU sintética de Valgrind y rastrea
validez de memoria. Cachegrind simula comportamiento de cache y branches para explicar
costos de localidad. Linux perf samplea eventos reales de hardware/software desde el lado
del kernel. La regla práctica es hacer la pregunta correcta: "¿este acceso de memoria es
válido?", "¿dónde gasta ciclos el programa?" o "¿qué comportamiento de cache/branches
domina?"
El reset: herramientas de corrección y herramientas de profiling no son intercambiables. Memcheck encuentra muchos bugs de memoria lentamente;
perfmide ejecución real con poco overhead; cachegrind da una simulación determinística útil, pero no es el hardware.
Cómo funciona realmente
Valgrind traduce machine code a una representación intermedia y lo corre bajo una herramienta. Memcheck rastrea definedness, addressability, allocations, frees y leaks. Por eso puede detectar lecturas no inicializadas e invalid frees en binarios que no compilaste con ASan. El costo es alto: los programas pueden correr decenas de veces más lento.
Cachegrind es otra herramienta de Valgrind. Registra instruction counts y simula comportamiento de cache y branches. Es excelente para comparar localidad algorítmica y hot spots de llamadas de forma repetible, pero es un modelo. Máquinas reales tienen prefetchers, caches compartidas, TLBs, cambios de frecuencia y ruido del OS.
perf es específico de Linux y usa performance events del kernel. Puede samplear CPU
cycles, instructions, cache misses, branches, page faults, context switches y call stacks.
Mide el binario real en la máquina real con mucho menos overhead que Valgrind, pero requiere
permisos Linux e interpretación cuidadosa.
| Herramienta | Mejor pregunta | Costo |
|---|---|---|
| Valgrind Memcheck | "¿Leí memoria inválida/no inicializada o filtré memoria?" | muy alto |
| Cachegrind | "¿Dónde están los costos de instructions/cache/branches en un modelo repetible?" | alto |
Linux perf stat |
"¿Qué contadores de eventos de hardware/software produjo esta corrida?" | bajo |
Linux perf record/report |
"¿Dónde cayó el tiempo sampleado?" | bajo a medio |
| Sanitizers | "¿La instrumentación del compilador puede detectar este bug en tests?" | medio |
Artefacto ejecutable: workload más disponibilidad local
El ejemplo vive en examples/toolchain-and-linking/valgrind-and-profilers-perf-cachegrind/.
Este entorno macOS no tiene Valgrind, Cachegrind ni Linux perf, así que el script construye
y corre un workload determinístico, y después reporta disponibilidad local de herramientas
en vez de inventar output.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
static uint64_t mix(uint64_t value) {
value ^= value >> 33;
value *= 0xff51afd7ed558ccdULL;
value ^= value >> 33;
value *= 0xc4ceb9fe1a85ec53ULL;
value ^= value >> 33;
return value;
}
int main(void) {
enum { count = 4096 };
uint64_t *values = malloc(count * sizeof(*values));
if (values == NULL) {
return 1;
}
for (int i = 0; i < count; i++) {
values[i] = mix((uint64_t)i + 1U);
}
uint64_t checksum = 0;
for (int pass = 0; pass < 8; pass++) {
for (int i = 0; i < count; i++) {
checksum ^= mix(values[i] + (uint64_t)pass);
}
}
printf("profile checksum: 0x%016llx\n", (unsigned long long)checksum);
free(values);
return 0;
}
Corrélo:
cd examples/toolchain-and-linking/valgrind-and-profilers-perf-cachegrind
./run.sh
Salida real de esta máquina:
== build workload ==
profile checksum: 0xa57dea46fbef3b53
== tool availability on this machine ==
valgrind: not installed on this machine
perf: not installed on this machine
cachegrind: not available without valgrind
En Linux con las herramientas instaladas, los comandos para correr contra el mismo workload son:
valgrind --tool=memcheck --leak-check=full ./demo
valgrind --tool=cachegrind ./demo
cg_annotate cachegrind.out.*
perf stat ./demo
perf record -g ./demo
perf report
En macOS, equivalentes aproximados son Instruments, sample, leaks, vmmap,
dtrace/DTrace donde esté permitido y la UI de profiling de Xcode. No son reemplazos
drop-in para Linux perf, y el soporte de Valgrind en macOS moderno es limitado.
Valgrind vs sanitizers
ASan suele correr mucho más rápido que Memcheck y detecta muchos bugs de memoria en loops de tests. Memcheck puede inspeccionar binarios no instrumentados y detecta usos de valores no inicializados que ASan no apunta igual. ASan cambia la compilación; Memcheck cambia la ejecución. Ninguno reemplaza disciplina de ownership ni tests.
TSan es el sanitizer para data races. Valgrind tiene Helgrind/DRD para análisis de threads, pero TSan suele ser la primera opción cuando el soporte de compiler/runtime está disponible. Para performance, usá profilers, no memory checkers.
Modos de falla y trade-offs
- Profilear debug builds miente.
-O0cambia la forma del programa. Profileá builds optimizados con símbolos, como-O2 -g. - Los microbenchmarks son frágiles. Frecuencia de CPU, warmup, estado de cache, tamaño de input y ruido del OS pueden dominar corridas chicas.
- Sampling necesita suficiente tiempo. Un programa que corre por 2 ms puede no producir
samples útiles con
perf record. - Valgrind cambia la ejecución. Es excelente para diagnostics de memoria, pero demasiado lento y sintético para conclusiones finales de performance.
- Cachegrind es un modelo. Tratálo como herramienta comparativa de localidad, no como modelo perfecto de tu CPU.
- Importan los permisos. Linux
perfpuede requerir cambios deperf_event_paranoido root para algunos eventos.
En la práctica
- Usá Memcheck cuando ASan no está disponible o necesitás detalle de lecturas no inicializadas/leaks.
- Usá ASan/UBSan primero en loops rápidos de CI. Encuentran muchos bugs de corrección antes.
- Usá
perf statantes deperf record. Los contadores de eventos te dicen si el programa es instruction-heavy, branchy, cache-missy o pesado en syscalls/context switches. - Usá Cachegrind para experimentos de localidad. Sirve cuando comparás dos data layouts o estructuras de loops.
- Siempre mantené símbolos. Builds optimizados con debug info vuelven legible la salida de profilers sin destruir la forma optimizada.
Conecta con: Toolchain y Linking · Sanitizers: ASan, UBSan y TSan · gdb / lldb esenciales · Data layout y cache-friendliness · Cache lines y locality
Fuentes
- Valgrind User Manual — arquitectura de Valgrind, comportamiento de Memcheck y uso de herramientas. https://valgrind.org/docs/manual/manual.html
- Valgrind Memcheck Manual — invalid accesses, valores no inicializados, leaks y diagnostics. https://valgrind.org/docs/manual/mc-manual.html
- Valgrind Cachegrind Manual — simulación de cache/branches,
cg_annotatee interpretación. https://valgrind.org/docs/manual/cg-manual.html - Linux
perfwiki —perf stat,perf record, modelo de eventos y overview de workflow. https://perfwiki.github.io/main/ - Brendan Gregg — Linux perf examples — patrones prácticos de comandos
perfe interpretación. https://www.brendangregg.com/perf.html