Introdução: por que o perfilamento não é um luxo, mas uma necessidade
\nTodo desenvolvedor C++ já passou pela situação em que o programa funciona, mas o faz lentamente. Palpites intuitivos sobre qual parte do código está "lenta" muitas vezes se mostram errados. Sem dados precisos, você corre o risco de otimizar o que já funciona rápido, perdendo de vista os verdadeiros "gargalos". O perfilamento é a única maneira científica de medir o desempenho e encontrar gargalos.
\nNeste artigo, abordaremos três ferramentas poderosas para perfilamento de código C++: gprof (estatístico), Perf (hardware) e Valgrind/Callgrind (simulação). Você aprenderá como aplicá-las na prática, interpretar os resultados e encontrar rapidamente as áreas problemáticas.
1. Perfilamento estatístico com gprof: início rápido
\ngprof é o profilador clássico do GNU, que funciona com base em amostragem estatística. Ele não requer privilégios especiais e é ideal para uma primeira análise.
Como preparar o código
\nPara usar o gprof, você precisa compilar o programa com as flags -pg e -g:
g++ -pg -g main.cpp -o my_program\n./my_program\nApós a execução do programa, um arquivo gmon.out será criado na pasta atual. Para obter um relatório legível, execute:
gprof my_program gmon.out > report.txt\n\nExemplo: encontrando o algoritmo lento
\nConsidere um programa simples que ordena um array de duas maneiras:
\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}\nExecute o perfilamento. No relatório do gprof, você verá uma tabela de chamadas. A bubble_sort ocupará 95-99% do tempo. Este é um gargalo óbvio — substituir a ordenação bolha por std::sort trará um ganho imediato.
Interpretação dos resultados
\nNo relatório, procure a coluna % time — ela mostra a proporção de tempo gasto na função. Funções com alta porcentagem são suas candidatas à otimização.
\n\n2. Perfilamento de hardware com Perf: precisão de ciclo
\nPerf é uma ferramenta moderna para Linux que usa contadores de hardware da CPU. Ela tem sobrecarga mínima e permite medir não apenas o tempo, mas também cache misses, branch misses e outras métricas.
Instalação e uso básico
\nsudo apt install linux-tools-common linux-tools-generic # Ubuntu/Debian\nperf stat ./my_program # Estatísticas gerais\nperf record ./my_program # Gravação do perfil\nperf report # Visualização do relatório\n\nExemplo: encontrando cache misses
\nSuponha que temos uma função que processa uma matriz linha por linha:
\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}\nExecute perf stat para cada função. Você verá que process_cols tem uma ordem de grandeza a mais de cache-misses. A razão