커널에서 가능성이 높고 가능성이 적은 통화는 무엇입니까? 커널 소스를 검색하는 동안이 문장을 찾았습니다.
# define likely(x) __builtin_expect(!!(x), 1)
# define unlikely(x) __builtin_expect(!!(x), 0)
누군가가 그것에 빛을 비출 수 있습니까?
커널에서 가능성이 높고 가능성이 적은 통화는 무엇입니까? 커널 소스를 검색하는 동안이 문장을 찾았습니다.
# define likely(x) __builtin_expect(!!(x), 1)
# define unlikely(x) __builtin_expect(!!(x), 0)
누군가가 그것에 빛을 비출 수 있습니까?
답변:
그것들은 GCC에 대한 컴파일러 힌트입니다. 분기가 수행 될 수 있는지 여부를 컴파일러에 알리기 위해 조건부로 사용됩니다. 컴파일러가 가장 빈번한 결과에 가장 적합한 방식으로 코드를 작성하도록 도와줍니다.
그들은 다음과 같이 사용됩니다 :
if (likely(some_condition)) {
// the compiler will try and make the code layout optimal for the case
// where some_condition is true, i.e. where this block is run
most_likely_action();
} else {
// this block is less frequently used
corner_case();
}
주의해서 사용해야합니다 (즉, 실제 분기 프로파일 링 결과를 기반으로). 잘못된 힌트는 성능을 저하시킬 수 있습니다 (분명히).
코드를 최적화하는 방법에 대한 몇 가지 예는를 검색하여 쉽게 찾을 수 있습니다 GCC __builtin_expect. 이 블로그 게시물 gcc 최적화 : 예를 들어 __builtin_expect 는 해체와 함께 분해를 자세히 설명합니다.
수행 할 수있는 최적화 종류는 프로세서마다 매우 다릅니다. 일반적인 아이디어는 프로세서가 코드 전체를 분기 / 점프하지 않으면 코드를 더 빠르게 실행한다는 것입니다. 선형 일수록, 예측 가능한 분기가 빠를수록 더 빨리 실행됩니다. (예를 들어 파이프 라인이 깊은 프로세서의 경우 특히 그렇습니다.)
따라서 컴파일러는 코드를 내 보냅니다. 예를 들어 대상 CPU가 선호하는 분기 일 경우 가장 많은 분기에 점프가 필요하지 않습니다.
GCC 4.8이 수행하는 작업을 확인하기 위해 디 컴파일하자
기대하지 않고
#include "stdio.h"
#include "time.h"
int main() {
/* Use time to prevent it from being optimized away. */
int i = !time(NULL);
if (i)
printf("%d\n", i);
puts("a");
return 0;
}
GCC 4.8.2 x86_64 Linux로 컴파일 및 디 컴파일 :
gcc -c -O3 -std=gnu11 main.c
objdump -dr main.o
산출:
0000000000000000 <main>:
0: 48 83 ec 08 sub $0x8,%rsp
4: 31 ff xor %edi,%edi
6: e8 00 00 00 00 callq b <main+0xb>
7: R_X86_64_PC32 time-0x4
b: 48 85 c0 test %rax,%rax
e: 75 14 jne 24 <main+0x24>
10: ba 01 00 00 00 mov $0x1,%edx
15: be 00 00 00 00 mov $0x0,%esi
16: R_X86_64_32 .rodata.str1.1
1a: bf 01 00 00 00 mov $0x1,%edi
1f: e8 00 00 00 00 callq 24 <main+0x24>
20: R_X86_64_PC32 __printf_chk-0x4
24: bf 00 00 00 00 mov $0x0,%edi
25: R_X86_64_32 .rodata.str1.1+0x4
29: e8 00 00 00 00 callq 2e <main+0x2e>
2a: R_X86_64_PC32 puts-0x4
2e: 31 c0 xor %eax,%eax
30: 48 83 c4 08 add $0x8,%rsp
34: c3 retq
메모리 명령 순서는 불변 : 먼저 printf다음 puts과 retq창.
기대
이제 다음으로 바꾸십시오 if (i):
if (__builtin_expect(i, 0))
그리고 우리는 :
0000000000000000 <main>:
0: 48 83 ec 08 sub $0x8,%rsp
4: 31 ff xor %edi,%edi
6: e8 00 00 00 00 callq b <main+0xb>
7: R_X86_64_PC32 time-0x4
b: 48 85 c0 test %rax,%rax
e: 74 11 je 21 <main+0x21>
10: bf 00 00 00 00 mov $0x0,%edi
11: R_X86_64_32 .rodata.str1.1+0x4
15: e8 00 00 00 00 callq 1a <main+0x1a>
16: R_X86_64_PC32 puts-0x4
1a: 31 c0 xor %eax,%eax
1c: 48 83 c4 08 add $0x8,%rsp
20: c3 retq
21: ba 01 00 00 00 mov $0x1,%edx
26: be 00 00 00 00 mov $0x0,%esi
27: R_X86_64_32 .rodata.str1.1
2b: bf 01 00 00 00 mov $0x1,%edi
30: e8 00 00 00 00 callq 35 <main+0x35>
31: R_X86_64_PC32 __printf_chk-0x4
35: eb d9 jmp 10 <main+0x10>
은 printf(컴파일 된 __printf_chk후), 함수의 끝으로 이동 한 puts다른 응답하여 한 바와 같이 분기 예측을 향상시키고 복귀.
따라서 기본적으로 다음과 같습니다.
int i = !time(NULL);
if (i)
goto printf;
puts:
puts("a");
return 0;
printf:
printf("%d\n", i);
goto puts;
이 최적화는로 수행되지 않았습니다 -O0.
그러나 CPU가__builtin_expect 없는 것보다 더 빠르게 실행되는 예제를 작성하면 행운이 있습니다. CPU는 그 당시에는 정말 똑똑했습니다 . 내 순진 시도는 여기에 있습니다 .
C ++ 20 [[likely]]및[[unlikely]]
C ++ (20)가 그 C ++ 기본 기능을 표준화했습니다 /programming/51797959/how-to-use-c20s-likely-unlikely-attribute-in-if-else-statement 그들은 가능성 것이다 (A 말장난!) 같은 것을하십시오.