안녕하세요. 오늘은 C언어를 사용하여 volatile 한정자를 알아보겠습니다.
volatile를 알아보기 전에 최적화를 알아야 합니다. 컴파일을 할 때 최적화(Optimization)이라는 기능이 있습니다. 최적화 기능을 사용하면 의미 없는 코드를 기계어로 변환할 때 코드를 무시해버리기도 해서 속도 향상이나 크기 줄이기가 가능합니다.
이런 식으로 컴파일러마다 최적화 옵션이 다 있습니다.
예를 들어서 최적화 기능을 켜고 아래의 코드를 글로 컴파일 해보겠습니다.
1
2
3
4
5
|
int i = 0;
i=1;
i=2;
i=3;
i=4;
|
cs |
2~4번 무시하고 i=4만 실행한다고 생각하면 됩니다 (만약 뒤의 코드에서 i를 사용하지 않으면 아무것도 안합니다)
volatile는 변수 앞에 쓰고 최적화를 하지 말라는 뜻입니다. 최적화 옵션을 키고 volatile를 변수 앞에 붙이면 최적화를 안 합니다. 예제로 확인해 보겠습니다.
예제1)
1
2
3
4
5
6
7
8
9
10
11
|
#include <stdio.h>
int main()
{
int i = 0;
i = 1;
i = 2;
i = 3;
i = 4;
printf("%d \n", i);
}
|
cs |
위의 코드는 i에 대해서 최적화를 하는 코드이고 i에 대해서 최적화를 안 하려면 volatile int i = 0; 하면 됩니다
아래의 코드는 최적화를 어셈블리어로 본 코드입니다. 어셈블리어로 보면 최적화를 어떻게 했는지 볼 수 있습니다. 어셈블리어를 몰라도 코드를 무시했는지 안 했는지만 보면 됩니다.
1
2
3
4
5
6
7
8
9
10
|
int i = 0; //차이점
i = 1;
i = 2;
i = 3;
i = 4;
printf("%d \n", i);
00601040 push 4
00601042 push offset string "%d \n" (0602100h)
00601047 call printf (0601010h)
0060104C add esp,8
|
cs |
1~5번 코드는 어셈블리어가 없습니다. 그냥 7번 줄에서 바로 4를 넣어버렸습니다
아래의 코드는 i만 최적화를 안 하겠다는 선언을 한 코드입니다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
volatile int i = 0; //차이점
001F1044 mov dword ptr [ebp-4],0
i = 1;
001F104B mov dword ptr [i],1
i = 2;
001F1052 mov dword ptr [i],2
i = 3;
001F1059 mov dword ptr [i],3
i = 4;
001F1060 mov dword ptr [i],4
printf("%d \n", i);
001F1067 mov eax,dword ptr [i]
001F106A push eax
001F106B push offset string "%d \n" (01F2100h)
001F1070 call printf (01F1010h)
001F1075 add esp,8
|
cs |
두 코드를 비교해 보면 volatile를 사용한 코드가 최적화를 안 하므로 더 깁니다. 또한 i에 일일이 상숫값을 넣어주는 작업이 어셈블리어로 표현되어 있습니다
(int i = 1; == 001F104B mov dword ptr [i],1 두 코드는 같은 의미입니다)
예제2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
#include <stdio.h>
#include <time.h>
int TESTCASE = 1000;
int TIME = 0;
clock_t start;
void Timer_Start()
{
start = clock();
}
void Timer_Stop()
{
TIME += ((int)clock() - start) / (CLOCKS_PER_SEC / 1000);
printf("TIME : %d ms\n", TIME);
}
int main()
{
Timer_Start();
volatile long long l = 0;
// long long l = 0;
long long j = 0;
printf("start \n");
for (l = 0; l < 2000000000; l++)
j++;
printf("end \n");
Timer_Stop();
}
|
cs |
21번줄,34번줄은 23~31줄 코드가 실행되는 시간을 측정해 주기 위한 함수입니다
23번째줄과 24번째줄을 각각 주석/주석해지하고 실행결과를 보겠습니다
volatile 사용(23번줄 주석해제, 24번줄 주석)
volatile 미사용(23번줄 주석, 24번줄 주석해제)
최적화를 시키면 0ms가 나오고 최적화를 안 시키면 5598ms가 나옵니다. 최적화를 시키면 0ms 나오는 이유가 28번째 줄은 j++를 시켜도 j를 어디에서 사용하지 않으므로 아예 실행을 안 합니다. 그러나 j를 사용하는 코드를 넣는다면(예로들어 printf("%d",j); ) 28번 줄은 실행이 되긴 하는데 최적화를 안 한 것보다는 빠릅니다
예제3)
MCU를 예로 들면 LED를 제어할때 핀에 0을 넣고 1을 넣습니다.
P=0;
P=1;
이런 식으로 LED를 제어하는데 최적화를 해버리면 원하는 대로 동작이 안됩니다. 그래서 MCU의 레지스터들은 전부 volatile를 붙여서 최적화를 안되게 합니다.
- ABOV, keil컴파일러 (sfr variables are always volatile. The compiler will not optimize accesses to this type of variables.)
-> sfr P0 = 0x80; // P0 Data Register
- Renesas
-> #define P0 (*(volatile __near unsigned char *)0xFF00)
'프로그래밍 언어 > C언어' 카테고리의 다른 글
gcc를 이용해서 빌드를 Make로 하기 (0) | 2024.07.14 |
---|---|
C언어 예시를 통해서 함수포인터 알아보기 (0) | 2022.03.04 |
C언어 Storage Class, Auto, Register, Extern, Static이란? (0) | 2022.01.10 |
C언어로 지수구하기 (0) | 2021.12.20 |
포인터 활용하기 (0) | 2021.08.07 |