본문 바로가기
Dev Language/Modern C++ (C++11, 14)

C++ 11 : 지연 평가

by 미티치 2020. 8. 8.

컴퓨터 프로그래밍에서 느긋한 계산법(Lazy evaluation)은 계산의 결과값이 필요할 때까지 계산을 늦추는 기법이다. (개념적인 부분)

느긋하게 계산하면 필요없는 계산을 하지 않으므로 실행을 더 빠르게 할 수 있고, 복합 수식을 계산할 때 오류 상태를 피할 수 있고, 무한 자료 구조를 쓸 수 있고, 미리 정의된 것을 이용하지 않고 보통 함수로 제어 구조를 정의할 수 있다.

 

이렇게 얘기하면 무슨말인지 와닿지 않을 것이다.

 

앞에 얘기는 다 까먹고 그냥 새롭게 얘기를 해보자. Effective C++ 에서는 '변수 정의는 최대한 늦출 수 있을때까지 늦춰라' 라는 말이 있는데,

보통 우리는 변수 선언은 함수의 최상단에 몰아놓는 경향이 있다. 코드의 가독성 때문이기도 하는데, ( 내 생각엔 학교에서 프로그래밍을 배울 때 그래왔던 관습때문에도 있다고 생각한다. ) 이렇게 함수 내부에서 언제쓰일지 모르는 변수를 위에 미리 써놓으면 안좋은 이유가 있다. 

void FunctionT( void )
{
    int    a, b, sum = 0;

    ...

    sum = a + b;

    ...
}

위와같은 100 줄짜리 FunctionT 함수가 있을 때 sum = a + b; 가 있는 라인이 65라인이었다고 쳐보자. (그리고 sum은 변수 선언 후 한번도 사용이 안된 변수라고 해보자)

그럼 sum은 함수의 첫줄에서 선언되어있고 컴퓨터가 a+b 연산을 해서 sum 에 저장하는 65 라인 이전까지 명령을 수행하는 동안 sum 은 한번도 안쓰였을 때, 만약 30번째 라인에서 exception이 발생하거나, (혹은 if문으로 어떤 조건에 의해) FunctionT 함수를 빠져나갔다.

그럼 sum은 결국 쓰이지도 않고 끝나버린 것이다.

 

이런 케이스가 생각보다 프로그래밍을 하면서 자주 하게되는 실수일 수 있다. 그놈의 코드 가독성이 뭐라고... (사실 나도 그럴 때 많음ㅠㅠ 가독성을 포기할 수가 없다.)

 

따라서 sum 이 실제로 사용되는 65라인까지 냅두지말고, sum을 사용하기 직전에 선언해서 사용을 하자. 이런 프로그래밍 습관이 더 좋을 것이다.

void FunctionT( void )
{
    int    a, b = 0;


    ...

    int    sum = 0;
    sum = a + b;


    ...
}

 

 


위에서 설명한 변수 선언을 최대한 늦추는 것도 지연 평가(Lazy evaluation) 기법을 코드에 녹이는 방법 중 하나이다.
이렇게 설명하면 조금 감이 오는가?

Lazy evaluation 은 소프트웨어의 성능 향상을 위한 기법인데, 프로그램에서 필요한 계산이 실질적으로 쓰이기 직전까지 미리 해두지 않고 최대한 미루는 (lazy 한) 기법을 의미한다. 이 기법으로 어떻게 성능을 향상시키냐고?

Lazy evaluation을 구현하는 기법은 주로 아래와 같은 방법으로 코드에 녹여서 성능을 향상시킬 수 있다.

 

  1. 레퍼런스 카운팅

    src 객체를 dst 객체에 복사할 때, src 객체가 변경되기 전까지 dst 객체는 실제 src를 다 복사해서 들고있는게 아니라 src 객체를 참조만 하고있다. 이렇게되면 실제로 (src가 변경되기 전까지는) dst는 메모리를 차지하지 않고 있지만, src를 모두 복사해서 가지고 있는 것처럼 동작한다. 

  2. Distinguishing Reads from Writes

    데이터의 읽기와 쓰기를 구분하는 것. 

 

더 참고할만한 URL
https://kkojabee.tistory.com/entry/More-Effective-C-Item-17-Consider-using-lazy-evaluation