Volatile

volatile

gcc compiler는 성능 향상(optimization)을 목적으로 경우에 따라 변수의 사용에 대해 무시하거나 사용 위치를 변경할 수 있는데 volatile을 사용하면 다음의  optimization을 하지 않고 코드를 만들어낸다.

  • Optimization case
    • 객체(변수)가 사용되지 않아도 된다고 판단할 때 무시한다.
    • 루프 문 내부에서 사용되는 객체(변수)가 input용도로만 사용되는 경우 루프문 바깥으로 이전한다.
  • 메모리, I/O 주소 등에 접근 시 생략될 가능성이 있거나 access 횟 수가 의도와 다르게 적게 호출될 가능성이 있는 경우 반드시 volatile을 사용하여 컴파일러로 하여금 관련 주소의 코드를 optimization 하지 않도록 해야 한다.

 

Using C

1) 무시되는 case

  • d1 값을 10번 읽어들일 때.

volatile-1.c

void discard1()
{
        int i;
        int d1 = 1;
        int sum = 0;

        for (i = 0; i < 10; i++)
                sum += d1;
}

void discard2()
{
        int i;
        volatile int d2 = 1;
        int sum = 0;

        for (i = 0; i < 10; i++)
                sum += d2;
}

int main()
{
        discard1();
        discard2();
}
  • 다음과 같이 disassemble 해보면 discard1() 함수의 경우 아무것도 하지 않음을 알 수 있다.
$ gcc -O2 volatile-1.c -o volatile-1
$ objdump -d volatile-1

(...생략...)
000083b4 <discard1>:
    83b4:       e12fff1e        bx      lr

000083b8 <discard2>:
    83b8:       e24dd008        sub     sp, sp, #8
    83bc:       e3a0300a        mov     r3, #10
    83c0:       e3a02001        mov     r2, #1
    83c4:       e58d2004        str     r2, [sp, #4]
    83c8:       e2533001        subs    r3, r3, #1
    83cc:       e59d2004        ldr     r2, [sp, #4]
    83d0:       1afffffc        bne     83c8 <discard2+0x10>
    83d4:       e28dd008        add     sp, sp, #8
    83d8:       e12fff1e        bx      lr
(...생략...)

 

2) 루프 밖으로 이동되는 케이스

 

#include <stdio.h>

int loop1(int * addr)
{
        int i;

        for (i = 0; i < 10; i++)
        {
                *addr += 1;
        }

        return i;
}

int loop2(volatile int * addr)
{
        int i;

        for (i = 0; i < 10; i++)
        {
                *addr += 1;
        }

        return i;
}

int main()
{
        int l1 = 0;
        volatile int l2 = 0;

        loop1(&l1);
        loop2(&l2);

        printf("l1=%d, l2=%d\n", l1, l2);
}

 

  • 다음과 같이 disassemble 해보면 loop1() 함수의 경우 10번 반복하지 않고 1번만 결과 값을 저장함을 알 수 있다.
$ gcc -O2 volatile-2.c -o volatile-2
$ objdump -d volatile-2

(...생략...)
00008408 <loop1>:
    8408:       e1a03000        mov     r3, r0
    840c:       e3a0000a        mov     r0, #10
    8410:       e5932000        ldr     r2, [r3]
    8414:       e0822000        add     r2, r2, r0
    8418:       e5832000        str     r2, [r3]
    841c:       e12fff1e        bx      lr

00008420 <loop2>:
    8420:       e3a0300a        mov     r3, #10
    8424:       e5902000        ldr     r2, [r0]
    8428:       e2533001        subs    r3, r3, #1
    842c:       e2822001        add     r2, r2, #1
    8430:       e5802000        str     r2, [r0]
    8434:       1afffffa        bne     8424 <loop2+0x4>
    8438:       e3a0000a        mov     r0, #10
    843c:       e12fff1e        bx      lr
(...생략...)
$ ./volatile-2
l1=10, l2=10

 

Using Inline Assembly

1) 무시되는 case

volatile-3.c

void discard3(int * input)
{
        int output;

        asm ("ldr %0, [%1]"
                        : "=r" (output)
                        : "r" (input)
                        : "cc");
}

void discard4(int * input)
{
        int output;

        asm volatile ("ldr %0, [%1]"
                        : "=r" (output)
                        : "r" (input));
}

int main()
{
        int d3 = 0;
        int d4 = 0;
        discard3(&d3);
        discard4(&d4);
}

 

  • 다음과 같이 disassemble 해보면 discard3() 함수의 경우 아무것도 하지 않음을 알 수 있다.
$ gcc -O2 volatile-3.c -o volatile-3
$ objdump -d volatile-3

(...생략...)
000083a4 <discard3>:
    83a4:       e12fff1e        bx      lr

000083ac <discard4>:
    83a8:       e5900000        ldr     r0, [r0]
    83ac:       e12fff1e        bx      lr
(...생략...)

 

2) 루프 밖으로 이동되는 케이스

volatile-4.c

int loop3(int * addr)
{
        int i;
        int tmp = 0;

        for (i = 0; i < 10; i++)
        {
                asm ("add %0, #1\n      str %0, [%1]"
                        : "=r" (tmp)
                        : "r" (addr)
                        : "memory");
        }
        return tmp;
}

int loop4(int * addr)
{
        int i;
        int tmp = 0;

        for (i = 0; i < 10; i++)
        {
                asm volatile ("add %0, #1\n     str %0, [%1]"
                        : "=r" (tmp)
                        : "r" (addr)
                        : "memory");
        }
        return tmp;
}

int main()
{
        int l3 = 1;
        int l4 = 1;

        loop3(&l3);
        loop4(&l4);
}
  • 1
$ gcc -O2 volatile-4.c -o volatile-4
$ objdump -d volatile-4

(...생략...)
000083cc <loop3>:
    83cc:       e3a0300a        mov     r3, #10
    83d0:       e2800001        add     r0, r0, #1
    83d4:       e5820000        str     r0, [r0]
    83d8:       e2533001        subs    r3, r3, #1
    83dc:       1afffffd        bne     83d8 <loop3+0xc>
    83e0:       e12fff1e        bx      lr

000083e4 <loop4>:
    83e4:       e3a0300a        mov     r3, #10
    83e8:       e2800001        add     r2, r2, #1
    83ec:       e5820000        str     r2, [r0]
    83f0:       e2533001        subs    r3, r3, #1
    83f4:       1afffffb        bne     83e8 <loop4+0x4>
    83f8:       e1a00002        mov     r0, r2
    83f8:       e12fff1e        bx      lr
(...생략...)

 

기타

댓글 남기기