Jun 15, 2016

C Programming #68: Volatile variable

C Programming #68: Volatile variable

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

No comments :

Post a Comment