Notice
Recent Posts
Recent Comments
Link
«   2026/05   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Archives
Today
Total
관리 메뉴

로또

Tail Call Optimization 본문

CS

Tail Call Optimization

아롱로또 2023. 9. 12. 18:53

작성 이유

퀵 정렬과 병합 정렬을 비교하던 중 퀵 정렬의 장점으로 TCO를 알게 되어 정리하려고 한다.

 

정의

재귀 함수의 성능을 향상시키고 Stack Overflow를 방지하는 최적화 기술

 

Tail Call 

함수의 호출이 끝난 후 현재 함수에서 추가 연산을 요구하지 않는 함수 형태이다.

어떠한 함수가 return 하기 직전에 호출되는 것을 의미한다.

아래와 같은 factorial 함수는 factorial(n-1) 함수 호출 이후에 n을 곱해주는 연산이 필요하므로 Tail Call이 아니다.

int factorial(int n) {
    if (n == 1) return 1;
    return n * factorial(n - 1);
}

 

factorial 함수를 다음과 같이 수정해보자.

tail_factorial(n-1) 호출 이후에 해당 함수에서는 별다른 연산없이 즉시 return하므로 Tail Call이다.

int tail_factorial(int n, int acc) {
    if (n == 1) return acc;
    return tail_factorial(n - 1, acc * n);
}

 

 

Tail Call Optimization

Tail Call 함수에서 새로운 Stack Frame을 생성하지 않고 동일한 Stack Frame에서 값만 대체하여 별도 공간의 할당없이 재사용하는 것이다. 

 

다음은 factorial함수를 Visual Studio의 디버거를 사용해 호출 스택을 조사한 결과이다.

factorial 함수의 호출 스택

호출 스택이 점점 쌓이는 것을 볼 수 있다.

 

다음은 tail_factorial 함수의 경우이다.

tail_factorial 함수의 호출 스택

분명 tail_factorial 함수를 재귀적으로 호출했음에도 호출 스택이 늘어나지 않는 모습을 볼 수 있다.

 

이처럼 TCO가 적용되면 Stack Overflow를 방지할 수 있으며 메모리를 효율적으로 사용할 수 있다.

 

퀵 정렬 함수 또한 함수의 마지막에서 추가 연산을 요구하지 않는 Tail Call이고, 때문에 TCO를 적용받을 수 있다.

 

Visual Studio에서 TCO 발생시키기

환경에 따라서 TCO를 지원하지 않을 수도 있어서 많은 자료를 참고했었다. 간단하게 정리하면 다음과 같다.

 

테스트 코드

#include <iostream>

using namespace std;

int factorial(int);
int tail_factorial(int, int);

int factorial(int n) {
    if (n == 1) return 1;
    return n * factorial(n - 1);
}

int tail_factorial(int n, int acc) {
    if (n == 1) return acc;
    return tail_factorial(n - 1, acc * n);
}

int main()
{
    // cout << factorial(5) << endl;
    cout << tail_factorial(5, 1) << endl;
    return 0;
}

프로젝트 - 속성 - C/C++ - 최적화 - 최적화(속도 우선)(/Ox) 선택

TCO를 적용하기 위한 설정

프로젝트 - 속성 - C/C++ - 코드 생성 - 기본 런타임 검사 기본값 선택

TCO를 적용하기 위한 설정

디버그 실행 후, 디버그 - 창 - 호출 스택

호출 스택 창 열기

위처럼 설정을 마치고, 중단점을 걸고 디버그를 하면 호출 스택을 검사할 수 있다.

 

참고자료

https://hongjw1938.tistory.com/192

 

알고리즘 - Quick(퀵) vs Merge(병합) 정렬(+TCO, 참조 지역성)

해당 포스팅은 표준(Standard)적인 퀵 / 병합 정렬의 경우에 대해 설명합니다. 각 정렬 방식의 응용에 따라 다양한 Variation이 있는 부분은 감안하지 않았습니다. 퀵 정렬과 병합 정렬 차이 우선 기본

hongjw1938.tistory.com

https://homoefficio.github.io/2015/07/27/%EC%9E%AC%EA%B7%80-%EB%B0%98%EB%B3%B5-Tail-Recursion/

 

재귀, 반복, Tail Recursion

fibonacci(피보나치) 수열잘 알려져 있듯 피보나치 수열은 다음과 같다. 10, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... 프로그래밍과는 관련 없는 얘기를 살짝 하자면, 이 수열의 이름은 본명은 Leonardo da Pisa이지만 fi

homoefficio.github.io

https://joooing.tistory.com/entry/%EC%9E%AC%EA%B7%80-%E2%86%92-%EA%BC%AC%EB%A6%AC-%EC%9E%AC%EA%B7%80-Tail-Recursion

 

꼬리 재귀 (Tail Recursion)

재귀 자체도 머리 속에 과정을 떠올리려고 하면 굉장히 추상적이라 어렵게 느껴지는데 꼬리 재귀 개념까지 한번에 이해하려고하니 글을 읽는 것만으로는 잘 이해가 되지 않았다. 개발자 도구를

joooing.tistory.com

https://bozeury.tistory.com/entry/%EA%BC%AC%EB%A6%AC-%EC%9E%AC%EA%B7%80-%EC%B5%9C%EC%A0%81%ED%99%94Tail-Recursion

 

꼬리 재귀 최적화(Tail Recursion)

재귀함수란 자기 자신을 호출하는 함수를 말합니다. 코드가 짧아져 가독성을 높일 수 있다는 장점이 있지만, 스택 오버 플로우를 일으킬 수 있는 엄청난 위험성도 내재하고 있습니다. 함수를 호

bozeury.tistory.com

https://yumere.tistory.com/69

 

꼬리재귀, Tail Recursion

Tail Recursion 일반적인 재귀함수는 특정 횟수 이상 호출 할 경우 Segment Fault를 출력하며 에러를 일으킨다. 하지만 꼬리재귀(Tail Recursion)은 이러한 문제점이 없다 프로그램을 실행하여 프로세스가

yumere.tistory.com

https://m.blog.naver.com/ydk928/60188058015

 

[C++] '/O2'과(와) '/RTC1' 명령줄 옵션이 호환되지 않습니다.

'/O2'과(와) '/RTC1' 명령줄 옵션이 호환되지 않습니다.     위 오류 메세지에서 '/O2...

blog.naver.com

 

'CS' 카테고리의 다른 글

new vs malloc  (0) 2023.10.27
비트 연산 정리  (1) 2023.10.09
참조 지역성  (0) 2023.09.12