Introducción: por qué el perfilado no es un lujo, sino una necesidad
\nCada desarrollador de C++ se ha enfrentado a la situación en la que un programa funciona, pero lo hace lentamente. Las conjeturas intuitivas sobre qué sección de código "se ralentiza" a menudo resultan erróneas. Sin datos precisos, corre el riesgo de optimizar lo que ya funciona rápido, pasando por alto los verdaderos "cuellos de botella". El perfilado es la única forma científica de medir el rendimiento y encontrar los puntos débiles.
\nEn este artículo, analizaremos tres potentes herramientas para el perfilado de código C++: gprof (estadístico), Perf (hardware) y Valgrind/Callgrind (simulación). Aprenderá a aplicarlos en la práctica, interpretar los resultados y encontrar rápidamente las secciones problemáticas.
1. Perfilado estadístico con gprof: inicio rápido
\ngprof es un perfilador GNU clásico que funciona basándose en el muestreo estadístico. No requiere permisos especiales y es ideal para un primer análisis.
Cómo preparar el código
\nPara que funcione gprof, debe compilar el programa con las banderas -pg y -g:
g++ -pg -g main.cpp -o my_program\n./my_program\nDespués de ejecutar el programa, aparecerá un archivo gmon.out en la carpeta actual. Para obtener un informe legible, ejecute:
gprof my_program gmon.out > report.txt\n\nEjemplo: encontrando un algoritmo lento
\nConsideremos un programa simple que ordena un array de dos maneras:
\n#include <iostream>\n#include <vector>\n#include <algorithm>\n#include <chrono>\n\nvoid bubble_sort(std::vector<int>& arr) {\n for (size_t i = 0; i < arr.size(); ++i)\n for (size_t j = 0; j < arr.size() - i - 1; ++j)\n if (arr[j] > arr[j+1])\n std::swap(arr[j], arr[j+1]);\n}\n\nvoid quick_sort_wrapper(std::vector<int>& arr) {\n std::sort(arr.begin(), arr.end());\n}\n\nint main() {\n std::vector<int> data(50000);\n std::generate(data.begin(), data.end(), std::rand);\n \n auto copy1 = data;\n auto copy2 = data;\n \n bubble_sort(copy1);\n quick_sort_wrapper(copy2);\n \n return 0;\n}\nEjecute el perfilado. En el informe de gprof verá una tabla de llamadas. bubble_sort ocupará el 95-99% del tiempo. Este es un cuello de botella obvio: reemplazar la ordenación de burbuja por std::sort dará una mejora instantánea.
Interpretación de resultados
\nEn el informe, busque la columna % time: muestra la proporción de tiempo empleado en la función. Las funciones con un alto porcentaje son sus candidatas para la optimización.
\n\n2. Perfilado por hardware con Perf: precisión a nivel de ciclo
\nPerf es una herramienta moderna para Linux que utiliza contadores de hardware de la CPU. Proporciona una sobrecarga mínima y permite medir no solo el tiempo, sino también fallos de caché, fallos de predicción de saltos y otras métricas.
Instalación y uso básico
\nsudo apt install linux-tools-common linux-tools-generic # Ubuntu/Debian\nperf stat ./my_program # Estadísticas generales\nperf record ./my_program # Grabación del perfil\nperf report # Visualización del informe\n\nEjemplo: encontrando fallos de caché
\nSupongamos que tenemos una función que procesa una matriz por filas:
\nconst int N = 4096;\nint matrix[N][N];\n\nvoid process_rows() {\n for (int i = 0; i < N; ++i)\n for (int j = 0; j < N; ++j)\n matrix[i][j] *= 2;\n}\n\nvoid process_cols() {\n for (int j = 0; j < N; ++j)\n for (int i = 0; i < N; ++i)\n matrix[i][j] *= 2;\n}\nEjecute perf stat para cada función. Verá que process_cols tiene un orden de magnitud más de cache-misses. La razón