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
위에서 언급 한 것뿐만 아니라 내 컴퓨터에서 측정 할 수있는 모든 캐시 누락을 검사했습니다 . 캐시 미스는 매우 시끄럽고 실행 시간과 거의 또는 전혀 상관이 없습니다.