커널에서 호출 가능성과 가능성이 적은 차이점은 무엇입니까?


11

커널에서 가능성이 높고 가능성이 적은 통화는 무엇입니까? 커널 소스를 검색하는 동안이 문장을 찾았습니다.

# define likely(x)      __builtin_expect(!!(x), 1)
# define unlikely(x)    __builtin_expect(!!(x), 0)

누군가가 그것에 빛을 비출 수 있습니까?


이것은 실제로 프로그래밍 질문이며 Stack OVerflow에 더 적합합니다 .
질 'SO- 악마 그만'

답변:


14

그것들은 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가 선호하는 분기 일 경우 가장 많은 분기에 점프가 필요하지 않습니다.


유니콘 은 무엇을 의미 합니까? 기술적 인 용어입니까, 아니면 필러입니까?
Sen

혼란을 피하기 위해 유니콘을 제거했습니다.
Mat

당신은에 정교한시겠습니까 시도하고 경우에 대한 코드 레이아웃 최적를 만들 것입니다 컴파일러 ? 그것이 어떻게되는지 알고 싶습니다.
Sen

그것에 약간의 정보를 추가했습니다. 코드를 최적화하는 일반적인 방법은 없으며 프로세서에 따라 다릅니다.
Mat

2

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다음 putsretq창.

기대

이제 다음으로 바꾸십시오 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 말장난!) 같은 것을하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.