코드가 꼬리 호출 최적화를 적극적으로 방지하려는 이유는 무엇입니까?


81

질문의 제목은 약간 이상 할 수 있지만 제가 아는 한 테일 콜 최적화에 반대하는 내용은 전혀 없습니다. 그러나 오픈 소스 프로젝트를 탐색하는 동안 컴파일러가 테일 호출 최적화를 수행하는 것을 적극적으로 중지하려는 몇 가지 기능 (예 : 이러한 해킹으로 가득 찬 CFRunLoopRef 구현)을 이미 발견했습니다 . 예를 들면 :

static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(CFRunLoopObserverCallBack func, CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
    if (func) {
        func(observer, activity, info);
    }
    getpid(); // thwart tail-call optimization
}

이것이 왜 그렇게 중요해 보이는지 알고 싶습니다. 일반 개발자 로서도이 점을 염두에 두어야하는 경우가 있습니까? 예 : 꼬리 호출 최적화에 일반적인 함정이 있습니까?


10
한 가지 가능한 함정은 애플리케이션이 여러 플랫폼에서 원활하게 작동하다가 테일 호출 최적화를 지원하지 않는 컴파일러로 컴파일 할 때 갑자기 작동을 멈춘다는 것입니다. 이 최적화는 실제로 성능을 향상시킬뿐만 아니라 런타임 오류 (스택 오버플로)를 방지 할 수 있습니다.
Niklas B.

5
@NiklasB. 그러나 이것이 비활성화하지 않는 이유가 아닙니까?
JustSid

4
시스템 호출은 TCO를 계산하는 확실한 방법 일 수 있지만 비용이 많이 드는 방법이기도합니다.
Fred Foo

39
적절한 논평을 할 수있는 좋은 기회입니다. 꼬리 호출 최적화를 방지하기 위해 해당 라인이있는 이유를 부분적으로 설명하는 경우 +1, 꼬리 호출 최적화를 처음에 비활성화해야하는 이유를 설명하지 않은 경우 -100 ...
Mark Sowul

16
의 값 getpid()이 사용되지 않기 때문에 정보에 입각 한 최적화 프로그램 ( getpid부작용이없는 것으로 알려진 함수 이기 때문에 )에 의해 제거 될 수 없으므로 어쨌든 컴파일러가 테일 호출 최적화를 수행 할 수 있습니까? 이것은 정말 깨지기 쉬운 메커니즘으로 보입니다 .
luiscubal

답변:


83

내 생각 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__에 디버깅 목적으로 스택 추적에 있는지 확인하는 것 입니다. 그것은이 __attribute__((no inline))이 아이디어를 백업한다.

아시다시피, 그 함수는 그냥 다른 함수로 바운스됩니다. 그래서 그것은 디버깅을 돕기 위해 그런 장황한 이름으로 만 생각할 수있는 트램폴린의 한 형태입니다. 이는 함수가 다른 곳에서 등록 된 함수 포인터를 호출하므로 해당 함수에 액세스 가능한 디버깅 기호가 없을 수 있다는 점을 고려할 때 특히 유용합니다.

유사한 작업을 수행하는 유사한 이름의 다른 함수도 주목하십시오. 역 추적에서 발생한 일을 확인하는 데 도움이되는 것 같습니다. 이것은 핵심 Mac OS X 코드이며 충돌 보고서 및 프로세스 샘플 보고서에도 표시됩니다.


예, __attribute__((noinline)). 나는 당신이 여기에 자리하고 있다고 생각합니다.
Niklas B.

네, 정말 말이됩니다. 그러나 이러한 함수가 어디에서 호출되는지 살펴보면 항상 하나의 함수에서만 호출된다는 것을 알 수 있습니다. 예를 들어 내 예제 함수는 __CFRunLoopDoObservers스택 추적에 확실히 표시되는 호출에서만 호출됩니다 .
JustSid

1
물론, 관찰자 콜백 / 블록 / 등이 실행되는 정확한 위치를 나타내는 또 다른 마커라고 생각합니다 .
mattjgalloway

2
이것이 최선의 답이라고 생각합니다. +1
R .. GitHub STOP HELPING ICE

@R .. 나는 단 하나의 대답 만 받아 들일 수 있고 Andrew White는 또한 꼬리 호출 최적화가 필요하지 않을 수있는 다른 사례를 명명했습니다. 나는 함수가 왜 그랬는지 묻지 않고 왜 일반적으로 바람직하지 않을 수 있는지 묻지 않았고 실제 예제로 함수를 제공했음을 기억하십시오.
JustSid

34

이것은 추측 일 뿐이지 만 스택 오버플로 오류로 인해 무한 루프 대 폭격을 피하기위한 것일 수 있습니다.

문제의 메서드가 스택에 아무것도 넣지 않기 때문에 꼬리 호출 재귀 최적화가 반환 주소를 스택에 넣는 최적화되지 않은 코드와는 반대로 무한 루프에 들어가는 코드를 생성하는 것이 가능해 보일 것입니다. 오용의 경우 결국 넘칠 것입니다.

내가 가진 유일한 생각은 디버깅 및 스택 추적 인쇄를 위해 스택에 대한 호출을 보존하는 것과 관련이 있습니다.


8
스택 추적 / 디버깅 설명이 훨씬 더 가능성이 있다고 생각합니다 (그리고 게시하려고했습니다). 무한 루프는 사용자가 응용 프로그램을 강제로 종료 할 수 있기 때문에 충돌보다 더 나쁘지는 않습니다. 그것은 또한 noinline을 설명합니다.
ughoavgfhw

3
@ughoavgfhw : 어쩌면 스레딩에 들어가면 무한 루프를 추적하기가 정말 어렵습니다. 나는 항상 오용이 예외를 유발해야한다는 사고 방식을 가지고있었습니다. 내가 이것을 할 필요가 없었기 때문에 여전히 추측 일뿐입니다.
Andrew White

1
동기화, 일종의 ... 난 방금 응용 프로그램이 새 창을 열도록 유지하는 나쁜 버그를 만났습니다. 이로 인해 응용 프로그램이 "힙"(내 메모리)을 포화시키고 X를 질식시키기 전에 충돌이 발생했다면 터미날로 전환하여 미친 응용 프로그램을 갑자기 종료 할 필요가 없었을 것입니다 (X가 곧 시작되었으므로 응답 없음). 그래서 아마도 스택 오버플로와 최적화없이 올 수있는 "Fail Fast"접근 방식을 선호하는 이유 일 것입니다 ...? 아니면 그냥 다른 문제 일 수도 있지만 ...!
ShinTakezou

2
@AndrewWhite 흠 저는 무한 루프를 완전히 좋아합니다. 디버그하기 쉬운 한 가지를 생각할 수 없습니다. 디버거를 연결하면 추측없이 문제의 정확한 위치와 상태를 얻을 수 있습니다. 그러나 사용자로부터 스택 추적을 얻으려면 무한 루프가 문제가 있다는 데 동의하므로 논리적으로 보입니다. 오류가 로그에 나타나고 무한 루프는 그렇지 않습니다.
Voo

1
이것은 함수가 처음에 재귀 적이라고 가정하지만 그렇지 않습니다. 직접적으로도, 간접적으로도 (함수가 나오는 컨텍스트를 살펴봄으로써). 나는 처음에 같은 잘못된 가정을했다.
Konrad Rudolph

21

잠재적 인 이유 중 하나는 디버깅 및 프로파일 링을 더 쉽게 만드는 것입니다 (TCO를 사용하면 상위 스택 프레임이 사라지고 스택 추적을 이해하기가 더 어려워 짐).


2
프로그램 속도를 늦추는 대신 프로파일 링을 더 쉽게 만드는 것은 좀 이상합니다. 차가 얼마나 멀리 갈 수 있는지 측정하기 전에 오일을 희석하는 것만 큼 의미가 있습니다. x
Matthieu M.

1
@MatthieuM .: 추가 된 호출이 루프에서 수백만 번 수행 된 경우 이러한 것은 의미가 없지만 초당 수백 번 이하로 실행되는 경우 실제 시스템에 그대로 두는 것이 좋습니다. 시스템을 제거하는 것보다 실제 시스템의 작동 방식을 조사 할 수 있고 그러한 제거로 인해 시스템 동작에 미묘하지만 중요한 변화가 발생할 위험이 있습니다.
supercat

@MatthieuM. 오일 희석이 모든 측정의 전제 조건이라면 실제로는 완벽합니다.
Dmitry Grigoryev 2016

@DmitryGrigoryev : 아니요, 그렇지 않습니다. 어떤 측정도 성가신 것은 아니지만 잘못된 측정은 쓸모없는 것에서 위험한 것입니다 (당신이 얼마나 신뢰하는지에 따라 다름). 오일 비유를 계속합니다. 속도가 느려지면 무게가 공기 역학보다 중요하다는 것을 나타내는 측정치를 얻을 수 있으므로 무게를 제거하고 공기 역학을 악화시켜 측정 한 내용을 최적화 할 수 있습니다. 더 빨리 가면 공기 역학이 더 중요하고 "개선"이 아무것도하지 않는 것보다 더 나쁘다는 것이 밝혀졌습니다!
Matthieu M.

@MatthieuM. 불확실성 원리에 대해 잘 알고 있습니까? 측정 대상과 상호 작용하지 않고는 아무것도 측정 할 방법이 없기 때문에 모든 측정은 어느 정도 잘못되었습니다. 따라서 예에서 오일을 변경하지 않더라도 자동차를 계측하면 공기 역학이 변경됩니다.
Dmitry Grigoryev
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.