간단 지식/System Programming

08. conditional expression - using branch or move

납작한돌맹이 2020. 11. 21. 02:31
반응형

어셈블러가 c코드로부터 jle, jne와 같은 명령어들을 산출해내는 이유는 변환하는 코드에 branch instruction이 있기 때문이다. 그러나 지금 이 시간은 개발자가 아니라 시스템 입장에서 코드를 봐야한다. 잦은 branch의 사용은 파이프라인 구조에 파괴적이기 때문에 CPU 관점에서 좋지 않다. 따라서 우리는 조건식을 branch가 아니라 move 명령어를 사용해서 동일한 효과를 낼 수 있다.

 

한가지 예제 코드를 보자.

long test(long x, long y){
        long result;
        if(x>y)
                result = x-y;
        else
                result = y-x;
        return result;
}

지난 글에도 사용했던 if-else문 예제이다. 보이다 싶이 L2로 이동하는 branch 명령어가 보인다. 이 부분을 없애기 위해 코드를 어떻게 고쳐야할까?

 

using branch using move
test:
        movq    %rdi, %rax
        cmpq    %rsi, %rdi
        jle     .L2
        subq    %rsi, %rax
        ret
.L2:
        subq    %rdi, %rsi
        movq    %rsi, %rax
        ret
test:
        movq %rdi, %rax
        subq %rsi, %rax 
        movq %rsi, %rdx
        subq %rdi, %rdx
------------------------------------
        cmpq %rsi, %rdi 
        cmovle %rdx, %rax
        ret

바로 if일 때와 else일 때 실행할 코드들을 우선적으로 다 처리한 후 cmpq 명령어로 x, y 값을 비교하는 것이다. 일단 %rdi = x, %rsi = y, %rax = result 이다. 점선을 기준으로 점선의 윗 부분에서 x-y, y-x를 실행한다. 따라서 현재 %rax = x-y, %rdx = y-x 이다. 그리고 점선 아래 부분에서 x, y를 비교하고, cmovle 명령어를 이용하여 x<=y라면 %rax에 %rdx를 대입하여 return하고, x>y라면 별다른 연산 없이 %rax를 return한다.

아까 말했듯 branch는 파이프라인 관점에서 그다지 좋지 못하다. 아쉽게도 conditional move에도 bad case가 존재한다.

1. expensive computation

2. risky computation

3. computation with side effects

'비용 높은 계산'은 거의 모든 조건식에 해당한다. branch를 사용하면 여러 조건 중 일부만 계산할 수 있지만 move를 사용하면 일단 모든 조건식에 해당하는 식을 처리하고 그 중 필요한 것만을 return하기 때문이다.

'위험한 계산'은 

'계산의 부작용'을 단적으로 보여주는 경우가 삼항연산자이다. 연산자에서 True일때 처리하는 식이 x*=7 이고 False일때 처리하는 식이 x+=3 이라고 가정해보자. 이런 경우에는 x+=7을 저장한 레지스터에 x+=3의 결과를 업데이트하는 문제가 생길 수 있다. 만에 하나 컴파일러가 올바른 판단을 해서 각각의 식의 결과를 다른 레지스터에 저장하면 문제가 없을 수 있다. 그러나 이런 경우 함수가 전역변수를 업데이트 하는 경우에 또다른 문제가 생길 수 있다. \

 

 

(이 글이 도움이 됐다면 광고 한번씩만 클릭 해주시면 감사드립니다, 더 좋은 정보글 작성하도록 노력하겠습니다 :) )

반응형