2009 년에 GCC (적어도 내 프로젝트와 컴퓨터에서)는 속도 ( 또는 ) 대신 크기 ( -Os
)를 최적화하면 눈에 띄게 더 빠른 코드를 생성하는 경향이 있으며 그 이후로 궁금해하고 있습니다.-O2
-O3
나는이 놀라운 행동을 보여주는 (어리석지 않은) 코드를 만들었고 여기에 게시하기에 충분히 작습니다.
const int LOOP_BOUND = 200000000;
__attribute__((noinline))
static int add(const int& x, const int& y) {
return x + y;
}
__attribute__((noinline))
static int work(int xval, int yval) {
int sum(0);
for (int i=0; i<LOOP_BOUND; ++i) {
int x(xval+sum);
int y(yval+sum);
int z = add(x, y);
sum += z;
}
return sum;
}
int main(int , char* argv[]) {
int result = work(*argv[1], *argv[2]);
return result;
}
로 컴파일하면 -Os
이 프로그램을 실행하는 데 0.38 초가 걸리 -O2
거나 or 로 컴파일되면 0.44 초가 걸립니다 -O3
. 이 시간은 일관되고 실질적으로 소음이 없습니다 (gcc 4.7.2, x86_64 GNU / Linux, Intel Core i5-3320M).
(업데이트 : 모든 어셈블리 코드를 GitHub 로 옮겼 습니다. 포스트가 부풀어 오르고 fno-align-*
플래그가 동일한 효과 를 가지므로 질문에 거의 가치가 없습니다 .)
불행하게도, 조립에 대한 이해는 매우 내가 아무 생각이 없다, 그래서 제한 여부를 나는 다음 한 정확 무엇입니까 : 나는에 대한 어셈블리를 잡고 -O2
및 위해 어셈블리로 모든 차이를 통합 -Os
제외.p2align
라인, 결과 여기 . 이 코드는 여전히 0.38s로 실행되며 유일한 차이점은 .p2align
것입니다.
올바르게 추측하면 스택 정렬을위한 패딩입니다. GCC 패드가 NOP와 함께 작동 하는 이유는 무엇입니까? 코드가 더 빨리 실행되기를 희망하지만 내 경우에는이 최적화가 역효과를 낳았습니다.
이 경우 범인이 패딩입니까? 왜 그리고 어떻게?
노이즈가 거의 발생하여 타이밍 미세 최적화가 불가능합니다.
C 또는 C ++ 소스 코드에서 미세 최적화 (스택 정렬과 관련이 없음)를 수행 할 때 실수로 운이 좋지 않은 정렬 / 불운 정렬이 방해받지 않도록하려면 어떻게해야합니까?
최신 정보:
다음 파스칼 Cuoq의 대답 나는 정렬과 조금 만지작 거렸다. -O2 -fno-align-functions -fno-align-loops
gcc 로 전달 하면 모든 .p2align
어셈블리에서 사라지고 생성 된 실행 파일은 0.38 초 안에 실행됩니다. gcc 문서 에 따르면 :
-Os는 모든 -O2 최적화를 활성화하지만 [O] -Os는 다음 최적화 플래그를 비활성화합니다.
-falign-functions -falign-jumps -falign-loops -falign-labels -freorder-blocks -freorder-blocks-and-partition -fprefetch-loop-arrays
따라서 (정렬) 정렬 문제처럼 보입니다.
Marat Dukhan의 답변-march=native
에서 제안한대로 여전히 회의적 입니다. 나는 이것이 (미스) 정렬 문제를 방해하는 것이 아니라고 확신하지 못한다. 내 컴퓨터에는 전혀 영향을 미치지 않습니다. (그럼에도 불구하고 나는 그의 대답을 찬성했다.)
업데이트 2 :
-Os
사진을 찍을 수 있습니다 . 다음 시간은
-
-O2 -fno-omit-frame-pointer
0.37 초 -
-O2 -fno-align-functions -fno-align-loops
0.37 초 -
-S -O2
0.37 초add()
후에 수동으로 어셈블리 이동work()
-
-O2
0.44 초
add()
전화 사이트와 의 거리가 중요합니다. 나는 시도 perf
했지만 출력 perf stat
과 perf report
나에게는 거의 이해가되지 않습니다. 그러나 나는 단 하나의 일관된 결과를 얻을 수있었습니다.
-O2
:
602,312,864 stalled-cycles-frontend # 0.00% frontend cycles idle
3,318 cache-misses
0.432703993 seconds time elapsed
[...]
81.23% a.out a.out [.] work(int, int)
18.50% a.out a.out [.] add(int const&, int const&) [clone .isra.0]
[...]
¦ __attribute__((noinline))
¦ static int add(const int& x, const int& y) {
¦ return x + y;
100.00 ¦ lea (%rdi,%rsi,1),%eax
¦ }
¦ ? retq
[...]
¦ int z = add(x, y);
1.93 ¦ ? callq add(int const&, int const&) [clone .isra.0]
¦ sum += z;
79.79 ¦ add %eax,%ebx
의 경우 fno-align-*
:
604,072,552 stalled-cycles-frontend # 0.00% frontend cycles idle
9,508 cache-misses
0.375681928 seconds time elapsed
[...]
82.58% a.out a.out [.] work(int, int)
16.83% a.out a.out [.] add(int const&, int const&) [clone .isra.0]
[...]
¦ __attribute__((noinline))
¦ static int add(const int& x, const int& y) {
¦ return x + y;
51.59 ¦ lea (%rdi,%rsi,1),%eax
¦ }
[...]
¦ __attribute__((noinline))
¦ static int work(int xval, int yval) {
¦ int sum(0);
¦ for (int i=0; i<LOOP_BOUND; ++i) {
¦ int x(xval+sum);
8.20 ¦ lea 0x0(%r13,%rbx,1),%edi
¦ int y(yval+sum);
¦ int z = add(x, y);
35.34 ¦ ? callq add(int const&, int const&) [clone .isra.0]
¦ sum += z;
39.48 ¦ add %eax,%ebx
¦ }
의 경우 -fno-omit-frame-pointer
:
404,625,639 stalled-cycles-frontend # 0.00% frontend cycles idle
10,514 cache-misses
0.375445137 seconds time elapsed
[...]
75.35% a.out a.out [.] add(int const&, int const&) [clone .isra.0] ¦
24.46% a.out a.out [.] work(int, int)
[...]
¦ __attribute__((noinline))
¦ static int add(const int& x, const int& y) {
18.67 ¦ push %rbp
¦ return x + y;
18.49 ¦ lea (%rdi,%rsi,1),%eax
¦ const int LOOP_BOUND = 200000000;
¦
¦ __attribute__((noinline))
¦ static int add(const int& x, const int& y) {
¦ mov %rsp,%rbp
¦ return x + y;
¦ }
12.71 ¦ pop %rbp
¦ ? retq
[...]
¦ int z = add(x, y);
¦ ? callq add(int const&, int const&) [clone .isra.0]
¦ sum += z;
29.83 ¦ add %eax,%ebx
add()
느린 경우에 전화를 걸고있는 것 같습니다 .
나는 내 컴퓨터에서 뱉어 낼 수있는 모든 것을 조사했다 perf -e
. 위에 주어진 통계 만이 아닙니다.
동일한 실행 파일의 stalled-cycles-frontend
경우 실행 시간과 선형 상관 관계가 표시됩니다. 나는 그토록 분명하게 상관되는 어떤 것도 눈치 채지 못했습니다. ( stalled-cycles-frontend
다른 실행 파일을 비교 하는 것은 의미가 없습니다.)
나는 첫 번째 의견으로 나온 캐시 미스를 포함시켰다. perf
위에서 언급 한 것뿐만 아니라 내 컴퓨터에서 측정 할 수있는 모든 캐시 누락을 검사했습니다 . 캐시 미스는 매우 시끄럽고 실행 시간과 거의 또는 전혀 상관이 없습니다.
답변
기본적으로 컴파일러는 “평균”프로세서를 최적화합니다. 프로세서마다 다른 명령어 시퀀스가 선호되므로 컴파일러 최적화를 -O2
통해 평균 프로세서에 유리하지만 특정 프로세서의 성능이 저하 될 수 있습니다 (및 동일 -Os
). 다른 프로세서에서 동일한 예제를 시도하면 일부 프로세서에서 이점을 얻는 -O2
반면 다른 프로세서에서는 -Os
최적화에 더 유리 하다는 것을 알 수 있습니다.
다음은 time ./test 0 0
여러 프로세서에 대한 결과입니다 (보고 된 사용자 시간).
Processor (System-on-Chip) Compiler Time (-O2) Time (-Os) Fastest
AMD Opteron 8350 gcc-4.8.1 0.704s 0.896s -O2
AMD FX-6300 gcc-4.8.1 0.392s 0.340s -Os
AMD E2-1800 gcc-4.7.2 0.740s 0.832s -O2
Intel Xeon E5405 gcc-4.8.1 0.603s 0.804s -O2
Intel Xeon E5-2603 gcc-4.4.7 1.121s 1.122s -
Intel Core i3-3217U gcc-4.6.4 0.709s 0.709s -
Intel Core i3-3217U gcc-4.7.3 0.708s 0.822s -O2
Intel Core i3-3217U gcc-4.8.1 0.708s 0.944s -O2
Intel Core i7-4770K gcc-4.8.1 0.296s 0.288s -Os
Intel Atom 330 gcc-4.8.1 2.003s 2.007s -O2
ARM 1176JZF-S (Broadcom BCM2835) gcc-4.6.3 3.470s 3.480s -O2
ARM Cortex-A8 (TI OMAP DM3730) gcc-4.6.3 2.727s 2.727s -
ARM Cortex-A9 (TI OMAP 4460) gcc-4.6.3 1.648s 1.648s -
ARM Cortex-A9 (Samsung Exynos 4412) gcc-4.6.3 1.250s 1.250s -
ARM Cortex-A15 (Samsung Exynos 5250) gcc-4.7.2 0.700s 0.700s -
Qualcomm Snapdragon APQ8060A gcc-4.8 1.53s 1.52s -Os
경우 gcc
에 따라 옵션 -mtune=native
또는을 사용하여 특정 프로세서에 대한 최적화를 요청 하여 불리한 최적화의 영향을 완화 할 수 있습니다 -march=native
.
Processor Compiler Time (-O2 -mtune=native) Time (-Os -mtune=native)
AMD FX-6300 gcc-4.8.1 0.340s 0.340s
AMD E2-1800 gcc-4.7.2 0.740s 0.832s
Intel Xeon E5405 gcc-4.8.1 0.603s 0.803s
Intel Core i7-4770K gcc-4.8.1 0.296s 0.288s
업데이트 : 아이비 브릿지 기반 코어는 세 가지 버전의 I3 gcc
( 4.6.4
, 4.7.3
,와 4.8.1
크게 다른 성능) 생산 바이너리를하지만, 어셈블리 코드는 미묘한 변화가있다. 지금까지이 사실에 대한 설명이 없습니다.
조립 gcc-4.6.4 -Os
(0.709 초 내에 실행) :
00000000004004d2 <_ZL3addRKiS0_.isra.0>:
4004d2: 8d 04 37 lea eax,[rdi+rsi*1]
4004d5: c3 ret
00000000004004d6 <_ZL4workii>:
4004d6: 41 55 push r13
4004d8: 41 89 fd mov r13d,edi
4004db: 41 54 push r12
4004dd: 41 89 f4 mov r12d,esi
4004e0: 55 push rbp
4004e1: bd 00 c2 eb 0b mov ebp,0xbebc200
4004e6: 53 push rbx
4004e7: 31 db xor ebx,ebx
4004e9: 41 8d 34 1c lea esi,[r12+rbx*1]
4004ed: 41 8d 7c 1d 00 lea edi,[r13+rbx*1+0x0]
4004f2: e8 db ff ff ff call 4004d2 <_ZL3addRKiS0_.isra.0>
4004f7: 01 c3 add ebx,eax
4004f9: ff cd dec ebp
4004fb: 75 ec jne 4004e9 <_ZL4workii+0x13>
4004fd: 89 d8 mov eax,ebx
4004ff: 5b pop rbx
400500: 5d pop rbp
400501: 41 5c pop r12
400503: 41 5d pop r13
400505: c3 ret
조립 gcc-4.7.3 -Os
(0.822 초에 실행) :
00000000004004fa <_ZL3addRKiS0_.isra.0>:
4004fa: 8d 04 37 lea eax,[rdi+rsi*1]
4004fd: c3 ret
00000000004004fe <_ZL4workii>:
4004fe: 41 55 push r13
400500: 41 89 f5 mov r13d,esi
400503: 41 54 push r12
400505: 41 89 fc mov r12d,edi
400508: 55 push rbp
400509: bd 00 c2 eb 0b mov ebp,0xbebc200
40050e: 53 push rbx
40050f: 31 db xor ebx,ebx
400511: 41 8d 74 1d 00 lea esi,[r13+rbx*1+0x0]
400516: 41 8d 3c 1c lea edi,[r12+rbx*1]
40051a: e8 db ff ff ff call 4004fa <_ZL3addRKiS0_.isra.0>
40051f: 01 c3 add ebx,eax
400521: ff cd dec ebp
400523: 75 ec jne 400511 <_ZL4workii+0x13>
400525: 89 d8 mov eax,ebx
400527: 5b pop rbx
400528: 5d pop rbp
400529: 41 5c pop r12
40052b: 41 5d pop r13
40052d: c3 ret
어셈블리 gcc-4.8.1 -Os
(0.994 초 내에 실행) :
00000000004004fd <_ZL3addRKiS0_.isra.0>:
4004fd: 8d 04 37 lea eax,[rdi+rsi*1]
400500: c3 ret
0000000000400501 <_ZL4workii>:
400501: 41 55 push r13
400503: 41 89 f5 mov r13d,esi
400506: 41 54 push r12
400508: 41 89 fc mov r12d,edi
40050b: 55 push rbp
40050c: bd 00 c2 eb 0b mov ebp,0xbebc200
400511: 53 push rbx
400512: 31 db xor ebx,ebx
400514: 41 8d 74 1d 00 lea esi,[r13+rbx*1+0x0]
400519: 41 8d 3c 1c lea edi,[r12+rbx*1]
40051d: e8 db ff ff ff call 4004fd <_ZL3addRKiS0_.isra.0>
400522: 01 c3 add ebx,eax
400524: ff cd dec ebp
400526: 75 ec jne 400514 <_ZL4workii+0x13>
400528: 89 d8 mov eax,ebx
40052a: 5b pop rbx
40052b: 5d pop rbp
40052c: 41 5c pop r12
40052e: 41 5d pop r13
400530: c3 ret
답변
동료가 내 질문에 대한 그럴듯한 답변을 찾도록 도와주었습니다. 그는 256 바이트 경계의 중요성을 알았습니다. 그는 여기에 등록되어 있지 않으며 나에게 직접 답을 게시하고 모든 명성을 얻도록 격려했다.
짧은 답변:
이 경우 범인이 패딩입니까? 왜 그리고 어떻게?
그것은 모두 정렬로 귀결됩니다. 정렬은 성능에 큰 영향을 줄 수 -falign-*
있으므로 처음에 플래그 가 있습니다 .
gcc 개발자에게 (거짓?) 버그 보고서를 제출 했습니다 . 기본 동작은 “ 기본적으로 루프를 8 바이트로 정렬하지만 10 바이트 이상을 채울 필요가없는 경우 16 바이트로 정렬하려고합니다.” 분명히이 기본값은이 특별한 경우와 내 컴퓨터에서 최선의 선택이 아닙니다. Clang 3.4 (트렁크) -O3
는 적절한 정렬을 수행하며 생성 된 코드에는이 이상한 동작이 표시되지 않습니다.
물론, 부적절한 정렬이 이루어지면 상황이 악화됩니다. 불필요하거나 잘못된 정렬은 이유없이 바이트를 소비하고 잠재적으로 캐시 미스 등을 증가시킵니다.
노이즈가 거의 발생하여 타이밍 미세 최적화가 불가능합니다.
C 또는 C ++ 소스 코드에서 미세 최적화 (스택 정렬과 관련이 없음)를 수행 할 때 이러한 우발적 인 운 / 불운 정렬이 방해받지 않도록하려면 어떻게해야합니까?
gcc에게 올바른 정렬을하도록 지시하면됩니다.
g++ -O2 -falign-functions=16 -falign-loops=16
긴 대답 :
다음과 같은 경우 코드가 느리게 실행됩니다.
-
XX
바이트 경계 삭감add()
중간에 (XX
기계 의존). -
호출이 바이트 경계
add()
를 뛰어 넘어야XX
하고 대상이 정렬되지 않은 경우 -
add()
정렬되지 않은 경우 . -
루프가 정렬되지 않은 경우
처음 2 개는 Marat Dukhan이 친절하게 게시 한 코드와 결과에서 아름답게 보입니다 . 이 경우 gcc-4.8.1 -Os
(0.994 초에 실행) :
00000000004004fd <_ZL3addRKiS0_.isra.0>:
4004fd: 8d 04 37 lea eax,[rdi+rsi*1]
400500: c3
256 바이트 경계 add()
는 중간에서 바로 자르고 add()
루프도 정렬 되지 않습니다. 놀랍게도, 이것은 가장 느린 경우입니다!
gcc-4.7.3 -Os
0.822 초로 실행되는 경우 256 바이트 경계는 콜드 섹션으로 만 절단됩니다 (그러나 루프도 add()
절단 되지도 않습니다 ).
00000000004004fa <_ZL3addRKiS0_.isra.0>:
4004fa: 8d 04 37 lea eax,[rdi+rsi*1]
4004fd: c3 ret
[...]
40051a: e8 db ff ff ff call 4004fa <_ZL3addRKiS0_.isra.0>
정렬 된 것이 없으며 add()
256 바이트 경계를 뛰어 넘어야합니다. 이 코드는 두 번째로 느립니다.
gcc-4.6.4 -Os
(0.709 초에 실행) 경우 정렬 된 것이 없지만 호출 add()
은 256 바이트 경계를 뛰어 넘을 필요가 없으며 대상은 정확히 32 바이트 떨어져 있습니다.
4004f2: e8 db ff ff ff call 4004d2 <_ZL3addRKiS0_.isra.0>
4004f7: 01 c3 add ebx,eax
4004f9: ff cd dec ebp
4004fb: 75 ec jne 4004e9 <_ZL4workii+0x13>
이것은 세 가지 중 가장 빠릅니다. 256 바이트 경계가 그의 컴퓨터에서 왜 특별합니까? 나는 그것을 알아 내기 위해 그에게 맡길 것입니다. 나는 그런 프로세서가 없습니다.
이제 내 컴퓨터 에서이 256 바이트 경계 효과를 얻지 못했습니다. 기능과 루프 정렬 만 내 컴퓨터에서 시작됩니다. 내가 통과 g++ -O2 -falign-functions=16 -falign-loops=16
하면 모든 것이 정상으로 돌아옵니다. 항상 가장 빠른 사례가 표시되고 시간은 -fno-omit-frame-pointer
더 이상 깃발에 민감하지 않습니다. 나는 통과 할 수 g++ -O2 -falign-functions=32 -falign-loops=32
또는 16의 배수, 코드는 그 중 하나에 민감하지 않습니다.
나는 2009 년에 gcc (적어도 내 프로젝트와 컴퓨터에서)가 속도 (-O2 또는 -O3) 대신 크기 (-Os)를 최적화하면 눈에 띄게 더 빠른 코드를 생성하는 경향이 있으며 궁금해했습니다. 그 이후로.
아마도이 예제의 것과 마찬가지로 정렬에 민감한 핫스팟이 있다고 설명합니다. 플래그 -Os
대신 엉망으로 ( -O2
) 대신 전달 하여 핫스팟이 우연히 운이 좋은 방식으로 정렬되어 코드가 빨라졌습니다. 크기를 최적화하는 것과는 아무런 관련이 없었습니다. 핫스팟이 더 잘 정렬되었다는 것은 우연히 우연이었습니다. 이제부터는 프로젝트에 대한 정렬 효과를 확인합니다.
아, 그리고 한가지 더. 예제에 표시된 것과 같은 핫스팟은 어떻게 발생할 수 있습니까? 그런 작은 함수의 인라인은 어떻게 add()
실패 할 수 있습니까?
이걸 고려하세요:
// add.cpp
int add(const int& x, const int& y) {
return x + y;
}
별도의 파일로 :
// main.cpp
int add(const int& x, const int& y);
const int LOOP_BOUND = 200000000;
__attribute__((noinline))
static int work(int xval, int yval) {
int sum(0);
for (int i=0; i<LOOP_BOUND; ++i) {
int x(xval+sum);
int y(yval+sum);
int z = add(x, y);
sum += z;
}
return sum;
}
int main(int , char* argv[]) {
int result = work(*argv[1], *argv[2]);
return result;
}
다음과 같이 컴파일됩니다 g++ -O2 add.cpp main.cpp
.
gcc는 인라인하지 않습니다 add()
!
그게 전부입니다. OP의 핫스팟을 의도 치 않게 생성하기가 쉽습니다. 물론 그것은 부분적으로 내 잘못입니다. gcc는 훌륭한 컴파일러입니다. 위와 같이 컴파일하면 : g++ -O2 -flto add.cpp main.cpp
즉, 링크 시간 최적화를 수행하면 코드가 0.19 초에 실행됩니다!
OP에서는 인라인이 인위적으로 비활성화되어 있으므로 OP의 코드가 2 배 느립니다.
답변
나는이 포스트-허용을 추가하여 큰 프로그램을 포함하여 프로그램의 전반적인 성능에 대한 정렬 효과가 연구되었음을 지적했다. 예를 들어, 이 기사 (그리고 CACM에도이 버전이 등장했다고 생각합니다)는 링크 순서와 OS 환경 크기 변경만으로 성능을 크게 전환하는 데 충분한 방법을 보여줍니다. 그것들은 이것을 “핫 루프”의 정렬에 기인합니다.
이 백서는 “눈에 띄지 않고 잘못된 데이터를 생성합니다!” 프로그램 실행 환경에서 거의 제어 할 수없는 차이로 인한 우연한 실험적 편향은 아마도 많은 벤치 마크 결과를 의미가 없게 만든다고 말합니다.
같은 관찰에서 다른 각도로 직면하고 있다고 생각합니다.
성능에 중요한 코드의 경우 설치 또는 런타임시 환경을 평가하고 다르게 최적화 된 주요 루틴 버전 중에서 가장 적합한 로컬 시스템을 선택하는 시스템에있어 이는 매우 좋은 주장입니다.
답변
나는 당신이 한 것과 같은 결과를 얻을 수 있다고 생각합니다.
-O2에 대한 어셈블리를 잡고 .p2align 행을 제외하고 모든 차이점을 -O에 대한 어셈블리에 병합했습니다.
…를 사용하여 -O2 -falign-functions=1 -falign-jumps=1 -falign-loops=1 -falign-labels=1
. 나는 이러한 옵션으로 모든 것을 컴파일 해왔으며 -O2
, 15 년 동안 측정하려고 할 때마다 평범한 것보다 빠릅니다 .
또한 완전히 다른 상황 (다른 컴파일러 포함) 의 경우 상황이 비슷하다는 것을 알았 습니다.“속도보다는 코드 크기를 최적화하는”옵션이 코드 크기와 속도를 최적화합니다.
올바르게 추측하면 스택 정렬을위한 패딩입니다.
아니요, 이것은 스택과 아무런 관련이 없으며 기본적으로 생성되는 NOP이며 -falign-* = 1 옵션은 코드 정렬을위한 것입니다.
GCC 패드가 NOP와 함께 작동하는 이유는 무엇입니까? 코드가 더 빨리 실행되기를 희망하지만 내 경우에는이 최적화가 역효과를 낳았습니다.
이 경우 범인이 패딩입니까? 왜 그리고 어떻게?
패딩이 범인 일 가능성이 높습니다. 패딩이 필요한 것으로 느껴지고 일부 경우에 유용한 이유는 코드가 일반적으로 16 바이트 라인으로 페치되기 때문입니다 ( 세부 사항은 프로세서 모델에 따라 다르므로 Agner Fog의 최적화 리소스 참조 ). 16 바이트 경계에서 함수, 루프 또는 레이블을 정렬하면 함수 또는 루프를 포함하기 위해 하나의 줄이 더 적게 필요할 가능성이 통계적으로 증가합니다. 이러한 NOP는 코드 밀도를 줄이고 따라서 캐시 효율성을 저하시키기 때문에 분명히 역효과를냅니다. 루프와 레이블의 경우, NOP는 한 번만 실행해야 할 수도 있습니다 (실행이 점프와는 반대로 루프 / 라벨에 정상적으로 실행되는 경우).
답변
프로그램이 CODE L1 캐시에 의해 제한되는 경우 크기 최적화가 갑자기 지불되기 시작합니다.
마지막으로 확인했을 때 컴파일러는 모든 경우에 이것을 알아낼만큼 똑똑하지 않습니다.
귀하의 경우 -O3은 아마도 두 개의 캐시 라인에 충분한 코드를 생성하지만 -Os는 하나의 캐시 라인에 적합합니다.
답변
나는 결코이 분야의 전문가는 아니지만, 현대 프로세서는 지점 예측과 관련하여 매우 민감하다는 것을 기억하는 것 같습니다 . 분기를 예측하는 데 사용되는 알고리즘은 대상의 거리와 방향을 포함하여 코드의 여러 속성을 기반으로합니다 (또는 적어도 어셈블러 코드를 작성한 날에 돌아 왔습니다).
떠오르는 시나리오는 작은 루프입니다. 지점이 뒤로 가고 거리가 너무 멀지 않은 경우 모든 작은 루프가이 방식으로 수행되므로 지점 예측이이 경우에 최적화되었습니다. 생성 된 코드 의 위치 add
와 work
생성 된 코드 의 위치를 바꾸 거나 두 위치가 약간 변경 될 때도 동일한 규칙이 적용됩니다.
즉, 나는 그것을 확인하는 방법을 모른다. 나는 이것이 당신이 조사하고 싶을 수도 있음을 당신에게 알리고 싶었다.