This article explores volatile variable. It is very important to understand usage of it. It is especially useful for C Program written for embedded system. This article tries to demonstate the usage of volatile variable by example.
Any variable can be made volatile by pre-pending volatile
keyword in front of it.
volatile int a; // a is volatile variable
The volatile keyword is intended to prevent any unwanted optimization done by compiler on access of variable which changes in way that compiler cannot predict. Lets understand the above statement part by part. Lets first understand optimization done by compiler on access of variable. If there is access to variable in the loop. e.g.
// y is some global variable int sum = 0; for(i = 0; i< 1000; i++) { sum = sum + y; }
The loop is run 1000 times, variable y is global variable, which is in main memory. As you would know access to main memory is slower when compared to access to CPU Registers. Hence to time optimize the loop compiler when converting above loop to assembly it would read y
and copy the content to some register r0
. And in loop instead of reading from y it would use the local copy of y which ins ro register. Optimization concept is very trivialized here for understanding. Now say this y
is in some other place in code either in ISR (Interrupt Service Routine) or in another thread. Now this change in y
cannot be predicted by compiler. Issue that would occur with this optimization is that loop would not get latest data but old data from r0. To prevent this unwanted optimization by compiler in accessing y
, we need to declare y as volatile. I will try to demo the above statement by writing a small C program and compiling assembly out of it with volatile and non-volatile variable.
int y = 10; int sum; int main() { for(int i = 0; i < 1000; i++) { sum = sum + y; } }
Now compiling the above program into assembly as follows
$ gcc -S volatile.c -O1
- -S option tells the compiler to compile C code into assembly
- -01 tells it to perform optimization.
The output of the above command is volatile.s
. I will only paste assembly of loop within main function.
main: .LFB0: .cfi_startproc movl sum(%rip), %ecx movl y(%rip), %edx movl $1000, %eax .L2: subl $1, %eax jne .L2 imull $1000, %edx, %eax leal (%rax,%rcx), %eax movl %eax, sum(%rip) movl $0, %eax ret
Lets not over analyze it. Can you see that ecx
is used to compute sum variable, edx
is used to store variable y. Loop is implemented using decrements rather than increment (which is written in C; this is again for better performance). Loop is implemented using jne L2
.As you see it loads y into edx
and goes on with the loop. In-fact it does just direct calculation of adding 10, 1000 times using imull
and leal
. One point is for sure inside loop latest value of y is not used. Now lets make y volatile.
volatile int y = 10; int sum; int main() { for(int i = 0; i < 1000; i++) { sum = sum + y; } }
Compiling by same method we get following assembly file
main: .LFB0: .cfi_startproc movl sum(%rip), %edx movl $1000, %eax .L2: movl y(%rip), %ecx addl %ecx, %edx subl $1, %eax jne .L2 movl %edx, sum(%rip) movl $0, %eax ret
If you see here it is now getting the latest value of y inside the loop. Hence the unwanted optimization is avoided.
Hope you liked the article, please spread the word around if you liked it.
Links
- Next Article - C Programming #69: Volatile pointers
- Previous Article - C Programming #67: Scope
- All Article - C Programming
No comments :
Post a Comment