Profiling C++ Code: Finding Bottlenecks in Practice

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why Profile C++ Code?



Optimization without measurements is guesswork. Even experienced developers often make mistakes when assuming which part of the code runs the slowest. Profiling is the only scientific way to answer the question: "Where exactly does the program spend the most time?"



In this article, we will explore practical tools for profiling C++ code on Linux: gprof, perf, and Valgrind (Callgrind). You will learn how to run profilers, interpret their reports, and find real bottlenecks.



1. Profiling with gprof



Preparing the Code



gprof is the simplest and most accessible profiler. To use it, you need to compile the program with the -pg flag:



g++ -pg -O2 main.cpp -o my_program


Run the program. It will execute slightly slower than usual but will create a gmon.out file with profiling data. Then run gprof:



./my_programgprof my_program gmon.out > report.txt


Analysis Example



Consider a simple program with two functions:



#include <iostream>#include <cmath>

void slowFunction() { double sum = 0.0; for (int i = 0; i < 100000000; ++i) { sum += std::sin(i) * std::cos(i); } std::cout << "Sum: " << sum << std::endl;}

void fastFunction() { int count = 0; for (int i = 0; i < 1000; ++i) { for (int j = 0; j < 1000; ++j) { count += (i * j) % 3; } } std::cout << "Count: " << count << std::endl;}

int main() { slowFunction(); fastFunction(); return 0;}


After running gprof, the report will show a table with the execution time of each function:



Flat profile:

Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls ms/call ms/call name 99.0 1.98 1.98 1 1980.0 1980.0 slowFunction() 1.0 0.02 0.02 1 20.0 20.0 fastFunction()


The conclusion is obvious: slowFunction() takes 99% of the time. The bottleneck is the call to the trigonometric functions sin and cos in the loop.



Limitations of gprof



  • Only works with programs compiled with -pg
  • Does not show time in inline functions
  • Not suitable for multithreaded applications (without additional configuration)


2. Profiling with perf (Linux)



Basic Run



perf is a powerful tool for profiling at the kernel level. It does not require recompilation of the program and works with any binaries (even with -O3 optimization):



# Installation (if not present)sudo apt install linux-tools-common linux-tools-generic

# Profiling with CPU cycle countingperf stat ./my_program

# Recording events for detailed analysisperf record ./my_programperf report


Example with perf stat



For the same program, perf stat will show:



Performance counter stats for './my_program':

1,987.45 msec task-clock # 1.000 CPUs utilized 10 context-switches # 5.028 /sec 0 cpu-migrations # 0.000 /sec 105 page-faults # 52.832 /sec 5,234,567,890 cycles # 2.634 GHz 9,876,543,210 instructions # 1.89 insn per cycle 1,234,567,890 branches # 621.234 M/sec 10,000,000 branch-misses # 0.81% of all branches

1.987456789 seconds time elapsed


Key m

Blogs

Book Recommendations