컴파일러가 모든 것을 인라인하지 않는 이유는 무엇입니까? [닫은]


13

때때로 컴파일러는 함수 호출을 인라인합니다. 즉, 호출 된 함수의 코드를 호출 함수로 이동합니다. 이렇게하면 통화 스택에서 물건을 밀거나 뺄 필요가 없기 때문에 약간 더 빨라집니다.

내 질문은, 왜 컴파일러가 모든 것을 인라인하지 않습니까? 실행 파일이 훨씬 빠르다고 가정합니다.

내가 생각할 수있는 유일한 이유는 상당히 큰 실행 파일이지만 요즘에는 수백 GB의 메모리가 중요합니까? 개선 된 성능이 가치가 없습니까?

컴파일러가 모든 함수 호출을 인라인하지 않는 다른 이유가 있습니까?


18
IDK는 당신에 관한 것이지만, 단지 수백 GB의 메모리가 없습니다.
Ampt

2
Isn't the improved performance worth it?루프를 100 번 실행하고 심각한 숫자를 처리하는 방법의 경우 2 개 또는 3 개의 인수를 CPU 레지스터로 이동하는 오버 헤드는 없습니다.
Doval

5
당신은 지나치게 일반적이며 "컴파일러"는 "모든 컴파일러"를 의미하고 "모든 것"은 실제로 "모든 것"을 의미합니까? 그렇다면 대답은 간단합니다. 단순히 인라인 할 수없는 상황이 있습니다. 재귀가 떠오른다.
Otávio Décio

17
캐시 위치는 작은 함수 호출 오버 헤드보다 훨씬 중요합니다.
SK-logic

3
오늘날 수백 개의 GFLOPS 처리 능력으로 성능 개선이 실제로 중요합니까?
mouviciel

답변:


22

먼저 인라인의 주요 효과 중 하나는 호출 사이트에서 추가 최적화가 가능하다는 것입니다.

귀하의 질문에 대해 : 인라인하기 어렵거나 불가능한 것들이 있습니다.

  • 동적으로 연결된 라이브러리

  • 동적으로 결정된 함수 (함수 포인터를 통해 호출 된 동적 디스패치)

  • 재귀 함수 (꼬리 재귀 캔)

  • 코드가없는 기능 (그러나 링크 시간 최적화는 일부 기능을 허용합니다)

그런 다음 인라인은 유익한 효과뿐만 아니라

  • 더 큰 실행 파일은 더 많은 디스크 공간과 더 큰로드 시간을 의미합니다

  • 실행 파일이 클수록 캐시 압력이 증가 함을 의미합니다 (간단한 게터와 같은 작은 기능을 충분히 줄이면 실행 파일 크기와 캐시 압력이 줄어들 수 있습니다)

마지막으로, 사소한 시간이 걸리지 않는 함수의 경우 이득은 그만한 가치가 없습니다.


3
일부 재귀 호출은 인라인 될 수 있지만 (꼬리 호출) 명시 적 스택을 선택적으로 추가하면 모두 반복으로 변환 될 수 있습니다.
ratchet freak

@ratchetfreak, 꼬리가 아닌 재귀 호출을 꼬리 하나로 변환 할 수도 있습니다. 그러나 그것은 "어려운"영역 (특히 동시 재귀 함수가 있거나 리턴을 시뮬레이션하기 위해 점프 할 위치를 동적으로 결정해야 할 때)의 영역에 있지만, 불가능하지는 않습니다 (연속 프레임 워크를 배치하고 현재를 고려하면 쉬워진다).
AProgrammer

11

주요 제한 사항은 런타임 다형성입니다. 작성할 때 동적 디스패치가 발생하면 foo.bar()메소드 호출을 인라인 할 수 없습니다. 이것은 컴파일러가 모든 것을 인라인하지 않는 이유를 설명합니다.

재귀 호출도 쉽게 인라인 할 수 없습니다.

크로스 모듈 인라이닝은 기술적 인 이유로 수행하기 어렵습니다 (예 : 증분 재 컴파일은 불가능)

그러나 컴파일러는 많은 것을 인라인합니다.


3
가상 디스패치를 ​​통한 인라인은 매우 어렵지만 불가능하지는 않습니다. 일부 C ++ 컴파일러는 특정 상황에서이를 수행 할 수 있습니다.
bstamour

2
...뿐만 아니라 일부 JIT 컴파일러 (비 가상화).
Frank

@bstamour 적절한 최적화가 설정된 언어의 반 괜찮은 컴파일러는 동적 유형을 컴파일 타임에 알 수있는 객체에서 선언 된 가상 메소드에 대한 호출을 정적으로 파견합니다. 이것은 (또는 다른) 인라이닝 단계 이전에 가상화 단계가 발생하면 인라이닝을 용이하게 할 수있다. 그러나 이것은 사소한 것입니다. 다른 의미가 있었습니까? 실제 "가상 디스패치를 ​​통한 인라인"을 달성 할 수있는 방법을 모르겠습니다. 즉 devirtualise - - 인라인에, 하나는 정적 유형을 알고 있어야 인라인 수단의 존재가 있도록 하지 아니 가상 파견
underscore_d

9

첫째, 항상 인라인 할 수는 없습니다. 예를 들어, 재귀 함수는 항상 인라이닝되지 않을 수 있습니다 (그러나 fact인쇄만으로 재귀 정의를 포함하는 프로그램 fact(8)은 인라인 될 수 있습니다).

그러면 인라인이 항상 유익하지는 않습니다. 컴파일러가 인라인을 너무 많이하여 결과 코드가 핫 파트가 L1 명령어 캐시에 맞지 않을 정도로 충분히 크면 인라인되지 않은 버전 (L1 캐시에 쉽게 맞음)보다 훨씬 느릴 수 있습니다 ... 또한, 최근의 프로세서는 CALL기계 명령 (적어도 알려진 위치, 즉 직접 호출, 포인터를 통한 호출이 아닌)까지 기계 명령 을 실행하는 데 매우 빠르다 .

마지막으로 전체 인라인에는 전체 프로그램 분석이 필요합니다. 이것은 가능하지 않거나 너무 비쌉니다. GCC (및 Clang / LLVM )에 의해 컴파일 된 C 또는 C ++ 을 사용하면 링크 타임 최적화 (예 : 컴파일 및 링크 )를 활성화해야하며 g++ -flto -O2컴파일 시간이 많이 걸립니다.


1
레코드의 경우 LLVM / Clang (및 기타 여러 컴파일러) 도 링크 타임 최적화를 지원합니다 .
당신은

나는 알고있다. LTO는 지난 세기 (IIRC, 적어도 일부 MIPS 독점 컴파일러에서는)에 존재했습니다.
Basile Starynkevitch

7

놀랍게도 모든 것을 인라인한다고해서 반드시 실행 시간이 단축되는 것은 아닙니다. 코드 크기가 커지면 CPU가 모든 코드를 캐시에 한 번에 유지하기가 어려울 수 있습니다. 코드의 캐시 미스가 더 커지고 캐시 미스가 비쌉니다. 잠재적으로 인라인 된 함수가 크면 훨씬 나빠집니다.

헤더 파일에서 '인라인'으로 표시된 코드를 대량으로 가져 와서 소스 코드에 넣음으로써 코드의 성능이 향상되었습니다. 따라서 코드는 모든 호출 사이트가 아닌 한 곳에만 있습니다. 그런 다음 CPU 캐시가 더 잘 사용되고 컴파일 시간이 더 좋아집니다 ...


이것은 단지 한 시간 전에 게시 된 이전 답변에서 언급 되고 설명 된 포인트를 반복하는 것 같습니다
gnat

1
어떤 캐시? L1? L2? L3? 어느 것이 더 중요합니까?
Peter Mortensen

1

모든 것을 인라인하면 디스크 메모리 소비가 증가하는 것이 아니라 내부 메모리 소비가 증가한다는 것을 의미합니다. 코드는 또한 코드 세그먼트의 메모리에 의존한다는 것을 기억하십시오. 함수가 10000 개 장소 (예 : 상당히 큰 프로젝트의 표준 라이브러리에있는 함수)에서 호출되면 해당 함수의 코드는 10000 배 더 많은 내부 메모리를 차지합니다.

또 다른 이유는 JIT 컴파일러 일 수 있습니다. 모든 것이 인라인이면 동적으로 컴파일 할 핫스팟이 없습니다.


1

하나, 모든 것을 인라인하면 매우 나쁘게 작동하는 간단한 예가 있습니다. 이 간단한 C 코드를 고려하십시오.

void f1 (void) { printf ("Hello, world\n"); }
void f2 (void) { f1 (); f1 (); f1 (); f1 (); }
void f3 (void) { f2 (); f2 (); f2 (); f2 (); }
...
void f99 (void) { f98 (); f98 (); f98 (); f98 (); }

모든 것이 당신에게 어떤 영향을 줄지 추측하십시오.

다음으로 인라인을 사용하면 작업 속도가 빨라진다는 가정을합니다. 때때로 그런 경우가 있지만 항상 그런 것은 아닙니다. 한 가지 이유는 명령 캐시에 맞는 코드가 훨씬 빠르게 실행되기 때문입니다. 10 곳에서 함수를 호출하면 항상 명령어 캐시에있는 코드를 실행합니다. 인라인 인 경우 사본이 모든 곳에 있으며 훨씬 느리게 실행됩니다.

인라인은 큰 기능을 생성합니다. 거대한 기능은 최적화하기가 훨씬 어렵습니다. 컴파일러가 함수를 인라인하지 못하게하기 위해 함수를 별도의 파일에 숨겨 성능에 중요한 코드가 크게 향상되었습니다. 결과적으로 이러한 함수에 대해 생성 된 코드는 숨겨 졌을 때 훨씬 더 좋았습니다.

BTW. "수백 GB의 메모리"가 없습니다. 제 작품 컴퓨터에는 "수백 GB의 하드 드라이브 공간"도 없습니다. 내 응용 프로그램이 "수백 GB의 메모리"인 경우 응용 프로그램을 메모리에로드하는 데 20 분이 걸립니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.