adding explaination of volatile
This commit is contained in:
@@ -11,27 +11,33 @@ $ sudo apt-get install binutils
|
|||||||
## Compiling a C++ Program for Profiling
|
## Compiling a C++ Program for Profiling
|
||||||
To use `gprof`, compile your program with the `-pg` flag:
|
To use `gprof`, compile your program with the `-pg` flag:
|
||||||
```sh
|
```sh
|
||||||
$ g++ -pg -o my_program my_program.cpp
|
$ g++ -pg -o test test.cpp
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example C++ Program
|
## Example C++ Program
|
||||||
Create a file `my_program.cpp` with the following code:
|
Create a file `test.cpp` with the following code:
|
||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <chrono>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
void slowFunction() {
|
void heavyComputation() {
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
volatile long long sum = 0;
|
||||||
|
for (long long i = 0; i < 500000000; ++i) {
|
||||||
|
sum += i; // Simple but expensive loop
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fastFunction() {
|
void lightComputation() {
|
||||||
for (volatile int i = 0; i < 1000000; ++i);
|
volatile int sum = 0;
|
||||||
|
for (int i = 0; i < 100000; ++i) {
|
||||||
|
sum += i; // Lighter loop
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
for (int i = 0; i < 5; ++i) slowFunction();
|
heavyComputation(); // Call heavy function once
|
||||||
for (int i = 100; i < 200; ++i) fastFunction();
|
for (int i = 0; i < 1000; ++i) {
|
||||||
|
lightComputation(); // Call light function many times
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -39,15 +45,15 @@ int main() {
|
|||||||
## Running and Profiling the Program
|
## Running and Profiling the Program
|
||||||
1. Compile the program:
|
1. Compile the program:
|
||||||
```sh
|
```sh
|
||||||
$ g++ -pg -o my_program my_program.cpp
|
$ g++ -pg -o test test.cpp
|
||||||
```
|
```
|
||||||
2. Execute the program to generate `gmon.out`:
|
2. Execute the program to generate `gmon.out`:
|
||||||
```sh
|
```sh
|
||||||
$ ./my_program
|
$ ./test
|
||||||
```
|
```
|
||||||
3. Analyze the profiling data:
|
3. Analyze the profiling data:
|
||||||
```sh
|
```sh
|
||||||
$ gprof my_program gmon.out > profile.txt
|
$ gprof test gmon.out > profile.txt
|
||||||
$ cat profile.txt
|
$ cat profile.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -62,3 +68,54 @@ int main() {
|
|||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
`gprof` is a powerful tool for detecting performance bottlenecks in C++ programs. By identifying expensive functions, developers can make targeted optimizations.
|
`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.
|
||||||
|
|||||||
Reference in New Issue
Block a user