다른 사람들이 말했듯이 프로그램의 성능을 먼저 측정해야하며 실제로 차이가 없을 것입니다.
아직도, 개념적 수준에서 나는 당신의 질문에 혼란 스러울 몇 가지를 정리할 것이라고 생각했습니다. 먼저, 당신은 묻는다 :
최신 컴파일러에서 함수 호출 비용이 여전히 중요합니까?
키워드 "function"과 "compilers"에 주목하십시오. 귀하의 견적은 미묘하게 다릅니다.
언어에 따라 메서드 호출 비용이 상당 할 수 있습니다.
이것은 객체 지향적 의미에서 메소드 에 대해 이야기하고 있습니다 .
"함수"와 "방법"은 종종 상호 교환 적으로 사용되지만 비용과 관련하여 (필요한) 비용과 컴파일과 관련하여 (주어진 맥락) 차이가 있습니다.
특히 정적 디스패치 와 동적 디스패치에 대해 알아야 합니다. 현재로서는 최적화를 무시하겠습니다.
C와 같은 언어에서 우리는 보통 static dispatch를 가진 함수 를 호출 합니다 . 예를 들면 다음과 같습니다.
int foo(int x) {
return x + 1;
}
int bar(int y) {
return foo(y);
}
int main() {
return bar(42);
}
컴파일러가 호출을 볼 때 foo(y)
해당 foo
이름이 참조 하는 함수를 알고 있으므로 출력 프로그램이 foo
함수로 바로 이동할 수 있으므로 상당히 저렴합니다. 이것이 정적 디스패치의 의미입니다.
대안은 동적 디스패치 인데, 컴파일러 는 어떤 함수가 호출되는지 알 수 없습니다 . 예를 들어, 다음은 Haskell 코드입니다 (C 코드가 복잡하기 때문에).
foo x = x + 1
bar f x = f x
main = print (bar foo 42)
여기서 bar
함수는 argument를 호출합니다 f
. 따라서 컴파일러는 bar
어디로 이동할지 모르기 때문에 빠른 점프 명령으로 컴파일 할 수 없습니다 . 대신에, 우리가 생성 한 코드 bar
는 역 참조 f
하여 어떤 함수를 가리키는 지 알아 낸 다음 점프합니다. 이것이 바로 동적 디스패치의 의미입니다.
이 두 예는 모두 기능을 위한 것 입니다. 동적으로 디스패치 된 특정 스타일의 함수로 생각할 수있는 메소드를 언급했습니다 . 예를 들어 다음은 Python입니다.
class A:
def __init__(self, x):
self.x = x
def foo(self):
return self.x + 1
def bar(y):
return y.foo()
z = A(42)
bar(z)
이 y.foo()
호출은 객체 에서 foo
속성 의 값을 찾고 y
찾은 것을 호출 하기 때문에 동적 디스패치를 사용합니다 . 그것은 그 모르는 y
클래스가됩니다 A
, 또는 것을 A
클래스가 포함 foo
방법을, 그래서 우리는 단지 똑바로로 이동할 수 없습니다.
이것이 기본 아이디어입니다. 정적 디스패치는 컴파일 또는 해석 여부에 관계없이 동적 디스패치보다 빠릅니다 . 다른 모든 것은 평등합니다. 역 참조는 어느 쪽이든 추가 비용이 발생합니다.
그렇다면 이것이 현대 최적화 컴파일러에 어떤 영향을 미칩니 까?
가장 먼저 주목해야 할 것은 정적 디스패치가 더 크게 최적화 될 수 있다는 것입니다. 우리가 어떤 함수로 점프하는지 알면 인라인과 같은 일을 할 수 있습니다. 동적 디스패치를 사용하면 런타임까지 점프하는 것을 알 수 없으므로 최적화가 그리 많지 않습니다.
둘째, 일부 언어 에서는 일부 동적 디스패치가 끝나는 위치 를 추론 하여 정적 디스패치로 최적화 할 수 있습니다. 이를 통해 인라인 등과 같은 다른 최적화를 수행 할 수 있습니다.
위의 파이썬 예제에서 파이썬은 다른 코드가 클래스와 속성을 재정의하도록 허용하기 때문에 그러한 추론은 절망적입니다.
예를 들어 어노테이션을 사용하여 y
클래스 로 제한 하여 더 많은 제한 A
을 적용 할 수있는 경우 해당 정보를 사용하여 대상 함수를 추론 할 수 있습니다. 서브 클래 싱 (클래스가있는 거의 모든 언어)이있는 언어에서는 y
실제로 다른 (하위) 클래스를 가질 수 있으므로 실제로는 충분하지 않으므로 final
어떤 함수가 호출되는지 정확히 알기 위해서는 Java 주석 과 같은 추가 정보가 필요합니다 .
하스켈은 OO 언어가 아니라 우리의 가치를 추론 할 수 f
인라인으로 bar
(하는 정적 으로 전달) main
대체 foo
를 위해 y
. foo
in 의 대상 main
이 정적으로 알려지기 때문에 호출이 정적으로 전달되고 아마도 인라인되고 완전히 최적화 될 것입니다 (이러한 함수가 작기 때문에 컴파일러가 인라인 할 가능성이 더 높습니다. 일반적으로 믿을 수는 없지만) ).
따라서 비용은 다음과 같습니다.
- 언어가 호출을 정적으로 또는 동적으로 전달합니까?
- 후자의 경우 언어는 구현이 다른 정보 (예 : 유형, 클래스, 주석, 인라인 등)를 사용하여 대상을 유추 할 수 있습니까?
- 정적 디스패치 (추론되거나 그렇지 않은)를 얼마나 적극적으로 최적화 할 수 있습니까?
동적 디스패치가 많고 컴파일러에서 사용할 수있는 보증이 거의없는 "매우 동적 인"언어를 사용하는 경우 모든 호출에 비용이 발생합니다. "매우 정적 인"언어를 사용하는 경우 성숙한 컴파일러는 매우 빠른 코드를 생성합니다. 당신이 사이에 있다면, 그것은 코딩 스타일과 구현이 얼마나 똑똑한 지에 달려 있습니다.