리눅스 커널 소스를 보다 보면 분기문(if)문에 linkely()/unlikely()함수가 사용되는 것을
볼 수 있을 것이다. 이는 성능향상을 꾀하려고 나온 방법인데..
정확한 명칭은 Branch Prediction 이라고 하며, 컴파일러에게 정보를 줘서 좀 더
효율적인 코드를 만들게끔 한다.
if( expr ) 에서 expr이 참(true)일 확률이 높으면 if( likely(expr) )를 사용하면 되고
거짓(false)일 확률이 높으면 if( unlikely(expr) )를 사용하면 된다.
이 경우 기존 브랜치 문으로 인해 CPU의 파이프라인이 깨지는 것을 방지할 수 있다.
이는 어디까지는 GCC 3.0 이상에서만 지원된다.
이호 wrote:
>
>첫손님이라 빨리 답변을 자세하게 드리죠~~ :)
>
>이런 것에 관심을 가지시는 분이 있네요. 이건 branch prediction에
>관련된 것입니다. gcc 3.0부터인가 branch prediction에 관련된
>builtin 함수들이 추가가 되었는데, likely()나 unlikely()는
>gcc 3.0 이상에서만 의미를 갖습니다.
>
>branch prediction에 대해서 조금 설명을 드리면. CPU가 명령어를
>실행할 때 pipeline이라는 것이 있습니다. 하나의 명령어을 실행하는
>과정을 3단계나 5단계로 나눌 수 있는데, 여러개의 명령어를 이어서
>실행할 때 여러 명령어가 pipeline의 다른 위치에서 다른 단계를
>거치게 됩니다. 즉 하나의 명령어를 가져와서 해석하고 실행하고
>결과를 저장한 후 다음 명령을 가져와 실행하는 것이 아니라, 하나의
>명령어를 실행하고 있을 때 이미 다음 명령어를 가져와서 해석하고
>있고, 바로 앞에 실행한 명령어를 결과를 저장하는 것입니다. 이렇게
>하면 이어진 명령어를 훨씬 빠른 시간에 처리할 수 있게 됩니다.
>그런데 if()문 등으로 인해 다른 코드로 건너뛰게 되는 경우
>pipeline의 효과를 별로 보지 못하게 됩니다. 다른 코드로 건너 뛰면
>pipeline을 다시 비워야 하기 때문이죠. 또한 이는 같은 cacheline에
>없을 가능성이 높기 때문에 cache에 명령어를 다시 가져와야할 수도
>있습니다. 이런 이유로 branch가 생기면 처리 속도가 저하가 됩니다.
>그렇다고 branch를 막을 수는 없기 때문에 대신 branch를 줄이는 것이
>좋습니다. 그리고 branch는 크게 두가지로 나눠볼 수 있습니다. 하나는
>conditional branch이고 다른 하나는 그냥 jump입니다. 앞의 경우는
>앞의 연산의 결과에 따라서 branch 여부가 정해지는 것이고, 뒤의 것은
>그냥 다른 곳으로 뛰는 것입니다. 뒤의 경우는 CPU가 이미 예측이 가능하기
>때문에 미리 준비할 수 있지만 앞의 경우는 그 명령어를 닥치기 전에
>알 수 가 없습니다. CPU에 따라서는 branch prediction을 해보려고
>하지만 완벽할 수가 없죠. 그래서 프로그래머가 주로 어느쪽으로 branch가
>일어날 것인지 예상할 수 있다면 그에 대한 정보를 컴파일러에게 줘서
>효율적인 코드를 만들어내게 하는 것입니다.
>
>그럼 간단한 예를 보겠습니다.
>
>if (expr)
> A
>or
> B
>C
>
>이런 코드가 있다면 컴파일러는
>
>expr 계산
>jump to B if false
>A
>jump C
>B
>C
>
>이런 코드도 만들 수 있고, 아니면
>
>expr 계산
>jump to A if true
>B
>jump C
>A
>C
>
>이런 코드도 만들 수 있습니다. 만약 expr이 참일 경우가 많다면
>앞의 코드가, 거짓일 경우가 많다면 뒤의 코드가 더 효율적이 됩니다.
>그래서 코드를 작성할 때 확률적으로 if(expr)의 expr이 참일
>확률이 월등이 높으면 likely()를, 거짓이 확률이 높으면 unlikely()를
>써서 컴파일러가 효율적으로 코드를 생성하도록 도와주는 것입니다.