Search⌘ K
AI Features

Measuring and Reasoning about Performance

Explore techniques to measure execution time accurately in C++ using <chrono>. Understand the challenges of parallel performance, including overhead and scalability limits explained by Amdahl's Law. Learn to benchmark sequential and parallel algorithms effectively and interpret results to guide optimization efforts.

We have learned how to leverage multi-core processors through parallel execution policies. However, simply adding std::execution::par does not automatically result in better performance. For small workloads, the overhead of thread creation, scheduling, and coordination can actually make parallel code slower than its sequential equivalent.

To write truly efficient software, we must move beyond intuition and rely on measurement. In this lesson, we will construct a reliable benchmarking approach using the C++ Standard Library and learn how to interpret performance results correctly.

High-resolution timing with <chrono>

C++ provides the <chrono> library for precise and reliable time measurement. Unlike older, C-style timing utilities, <chrono> is both type-safe and platform-independent. It enforces time units at the type level, preventing mistakes such as mixing seconds and milliseconds.

To measure execution time, we record the current time immediately before and after a block of code using std::chrono::high_resolution_clock. The difference between these two time points yields a duration, representing how long the operation took to execute. This duration can then be converted into a desired unit, such as milliseconds or microseconds, using a duration cast to produce a clear and human-readable measurement.

Look at an example below.

C++ 23
#include <iostream>
#include <vector>
#include <algorithm>
#include <chrono> // Required for timing
#include <thread>
void heavy_computation() {
// Simulate work by sleeping for 100ms
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
int main() {
// 1. Capture the start time
auto start = std::chrono::high_resolution_clock::now();
// 2. Run the code we want to measure
heavy_computation();
// 3. Capture the end time
auto end = std::chrono::high_resolution_clock::now();
// 4. Calculate the duration
auto duration = end - start;
// 5. Convert to milliseconds for printing
// We use duration_cast to convert the precise internal tick count to milliseconds
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
std::cout << "Operation took " << ms.count() << "ms\n";
return 0;
}

Let’s break this down step by step:

  • Line 4: We include <chrono>, which provides clock utilities and duration types for measuring time.

  • Line 14: We call high_resolution_clock::now(), which returns a time_point representing the current instant according to that clock.

  • Line 23: We subtract the two time_point values, producing a duration that represents the elapsed time between them.

  • Line 27: We use duration_cast to convert the measured duration into a specific unit, such as std::chrono::milliseconds.

  • Line 29: ...