환경은 Ubuntu 에서 진행하였다.
최적화 옵션 : -O0, -O1, -O2, -O3, -Ofast, -Os, -Oz
-O0 : 최적화가 없고 디버깅의 유리-O1 : 기본적인 최적화 -O2 : 가장 많이 사용하는 최적화로 -O1에 추가적인 최적화 적용-O3 : -O2보다 적극적인 최적화 수행-Ofast : -O3 + 엄격한 표준 준수를 무시하는 최적화-Os : 실행 속도보다 실행파일 크기 최소화가 목적-Oz(Clang) : -Os 보다 더 강력한 크기 최적화 수행| 옵션 | 속도 최적화 | 크기 최적화 | 디버깅 가능성 | 특징 |
|---|---|---|---|---|
-O0 | 느림 | 크다 | 디버깅 용이 | 최적화 없음 |
-O1 | 약간 빠름 | 약간 작음 | 디버깅 가능 | 기본적인 최적화 |
-O2 | 빠름 | 보통 | 디버깅 어려움 | 일반적인 최적화 |
-O3 | 매우 빠름 | 보통 | 디버깅 매우 어려움 | 적극적인 최적화, 루프 벡터화 |
-Ofast | 최고 속도 | 보통 | 디버깅 거의 불가능 | 수학적 정확성 일부 희생 |
-Os | 보통 | 작음 | 디버깅 어려움 | 코드 크기 최적화 중점 |
-Oz | 보통 | 매우 작음 | 디버깅 어려움 | Clang 적용, 최대 크기 최적화 |
-O2옵션으로 gcc, clang으로 컴파일 하여 비교해 보기
코드는 while문 안에 switch문이 들어있는 코드를 사용

코드 동작은 0부터 9까지 짝수와 홀수를 구하여 출력하는 코드이다.
gcc -O2 -S test.c -o gcc_O2.s
gcc를 이용하여 컴파일한 main문 결과이다.
맨 처음 ecx, edx, eax를 0으로 초기화 시켜준다.
이후l4로 점프하여 test를 통해 최하위 비트가 1인지 확인하여 홀수, 짝수를 구분한다.
홀수라면l9으로 점프하여 eax(i)값을 증가 시키고, ecx값을 1 증가시킨다.
홀수일때 ecx를 증가시키므로 이는 변수 odd임을 유추할 수 있다.
.L4에서 짝수인 경우에는 점프를 하지 않고 eax, edx를 증가 시키고 edx는 변수 even임을 알 수 있다.
i가 10이 되면 출력문으로 jmp하는 조건문은 l9, l4에 존재하는 것을 알 수 있다.
clang -O2 -S test.c -o clang_O2.s
같은 c코드를 clang을 이용하여 컴파일 해 보았다.
main만 캡처한 사진이다.
gcc와 비교했을때 확연히 길이가 줄어든 것을 볼 수 있다.
특이한 점은 결과값을 계산하여 레지스터에 저장하고 바로 print를 하였다는 점이다.
이를 통해 clang이 gcc에 비해 최적화가 더 잘 되는 것을 볼 수 있었다.
이번에는 GCC의 옵션을 달리 하여 결과값을 비교해 보도록 하자.
gcc -O0 -S test.c -o gcc_O0.s
-O2옵션을 사용했을 때 보다 훨신 길어진 것을 볼 수 있다.
먼저 i, odd, even 변수의 값들을 스택에 저장을 한다.movl -12(%rbp), %edx movl %edx, %eax sarl $31, %eax shrl $31, %eax addl %eax, %edx andl $1, %edx subl %eax, %edx movl %edx, %eax testl %eax, %eax je .L3이 부분이 나머지를 구하는 부분인데 eax에 -12(%rbp)를 저장하고
최상위 비트(부호비트)를 제외하고 0으로 만들어 준다.
그 다음 edx에 eax의 부호비트를 저장하고 edx의 최하위 비트를 제외하고 0으로 만들어 준다.
다시 부호비트를 제거하고 test를 통해 나머지가 1인지 확인하여 점프한다.
정리하면 음수의 상황을 고려하여 shift 연산을 이용하여 나머지를 구하는 코드이다.
l3, l4 는 각각 odd, even을 증가시켜주는 부분이다.
이번에는
-Ofast옵션을 이용하여 컴파일 해 보았다.
gcc -Ofast -S test.c -o gcc_Ofast.s
결과는 clang으로 컴파일 했을때와 동일한 것을 볼 수 있다. 레지스터에 출력값 5씩 저장하고 print 함수를 호출한다.
이번에는
-Oz옵션을 사용하여 분석해 보았다.
gcc -Oz -S test.c -o gcc_Oz.s
루프는 그대로 유지된 것을 볼 수 있으며, 동작은-O2옵션을 사용했을 때와 유사하다.
-O2과 비교를 해보자 먼저-O2
종료 조건을 두 번 확인하는 것을 볼 수 있다.
이번엔-Oz옵션을 살펴보자
루프의 종료조건을 한 번 확인하는 것을 볼 수 있다.
그 외에도add명령어 대신inc명령어를 사용하거나 분기문을 최소화한 것을 볼 수 있었다.
time명령어를 통해 실행파일의 동작 시간을 확인할 수 있다.
명확한 시간 확인을 위해 while문의 반복 횟수 100,000,000으로 증가하여 진행해 보았다.

실행파일 생성 후

시간을 확인한 결과이다.
코드 실행시간이 많이 길지 않아 명확한 비교를 하긴 어렵다.
-O0이 가장 오래 걸린 것을 볼 수 있음Ofast가 가장 빠름
코드의 길이가 길지 않고 복잡하지 않아 크기의 큰 변화는 보기 어렵다.
하지만 Ofast옵션의 크기가 가장 큰 것은 뚜렷하게 볼 수 있었다.