From 33c771a3a5ce623f0aad2e00a89068e6204b8b14 Mon Sep 17 00:00:00 2001 From: Jidong Xiao Date: Mon, 24 Feb 2025 22:17:23 -0500 Subject: [PATCH] adding explaination of volatile --- lectures/optimization/gprof/README.md | 83 ++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 13 deletions(-) diff --git a/lectures/optimization/gprof/README.md b/lectures/optimization/gprof/README.md index 5c6a1e3..067b225 100644 --- a/lectures/optimization/gprof/README.md +++ b/lectures/optimization/gprof/README.md @@ -11,27 +11,33 @@ $ sudo apt-get install binutils ## Compiling a C++ Program for Profiling To use `gprof`, compile your program with the `-pg` flag: ```sh -$ g++ -pg -o my_program my_program.cpp +$ g++ -pg -o test test.cpp ``` ## Example C++ Program -Create a file `my_program.cpp` with the following code: +Create a file `test.cpp` with the following code: ```cpp #include -#include -#include -void slowFunction() { - std::this_thread::sleep_for(std::chrono::seconds(1)); +void heavyComputation() { + volatile long long sum = 0; + for (long long i = 0; i < 500000000; ++i) { + sum += i; // Simple but expensive loop + } } -void fastFunction() { - for (volatile int i = 0; i < 1000000; ++i); +void lightComputation() { + volatile int sum = 0; + for (int i = 0; i < 100000; ++i) { + sum += i; // Lighter loop + } } int main() { - for (int i = 0; i < 5; ++i) slowFunction(); - for (int i = 100; i < 200; ++i) fastFunction(); + heavyComputation(); // Call heavy function once + for (int i = 0; i < 1000; ++i) { + lightComputation(); // Call light function many times + } return 0; } ``` @@ -39,15 +45,15 @@ int main() { ## Running and Profiling the Program 1. Compile the program: ```sh - $ g++ -pg -o my_program my_program.cpp + $ g++ -pg -o test test.cpp ``` 2. Execute the program to generate `gmon.out`: ```sh - $ ./my_program + $ ./test ``` 3. Analyze the profiling data: ```sh - $ gprof my_program gmon.out > profile.txt + $ gprof test gmon.out > profile.txt $ cat profile.txt ``` @@ -62,3 +68,54 @@ int main() { ## Conclusion `gprof` is a powerful tool for detecting performance bottlenecks in C++ programs. By identifying expensive functions, developers can make targeted optimizations. + +# Why Use `volatile` in the above program? + +## Compiler May Remove the Loops +When compiling a program, the compiler applies optimizations to make the code run faster. One such optimization is **dead code elimination**, where the compiler removes code that does **not affect the program's observable behavior**. + +For example, consider this function: + +```cpp +void heavyComputation() { + long long sum = 0; + for (long long i = 0; i < 500000000; ++i) { + sum += i; + } +} +``` + +- The compiler notices that `sum` is **never used outside the function**. +- Since the result is discarded, the compiler **may completely remove the loop**. +- This means `heavyComputation()` might do **nothing** at runtime, which ruins our profiling experiment. + +--- + +## How Does `volatile` Help? +Declaring a variable as `volatile` tells the compiler: + +"This variable might change in ways you cannot predict, so do not optimize it away." + +For example: + +```cpp +void heavyComputation() { + volatile long long sum = 0; // Mark sum as volatile + for (long long i = 0; i < 500000000; ++i) { + sum += i; + } +} +``` + +- Now, **even if `sum` is never used**, the compiler **must** perform the loop. +- The `volatile` keyword prevents the compiler from assuming that `sum` is unimportant. +- This ensures that the loop actually runs during profiling. + +--- + +## **Does `volatile` Affect Performance?** +Yes, but only slightly. +- **Without `volatile`**, the compiler can optimize the loop aggressively. +- **With `volatile`**, every read and write to `sum` is guaranteed to happen exactly as written, preventing some optimizations. + +However, this small cost is **worth it for benchmarking**, because it ensures that the loops are not removed.