함수 호출은 성능에 얼마나 영향을 줍니까?


13

메소드 또는 함수로 기능을 추출하는 것은 특히 OOP에서 코드 모듈화, 가독성 및 상호 운용성을 위해 반드시 필요합니다.

그러나 이것은 더 많은 함수 호출이 이루어질 것임을 의미합니다.

코드를 메소드 또는 함수로 분리하면 실제로 현대 언어의 성능에 어떤 영향을 미칩니 까?

* 가장 인기있는 것 : C, Java, C ++, C #, Python, JavaScript, Ruby ...



1
소금의 가치가있는 모든 언어 구현은 현재 수십 년 동안 인라이닝을 해왔다고 생각합니다. IOW, 오버 헤드는 정확히 0입니다.
Jörg W Mittag

1
"더 많은 함수 호출이 이루어질 것"은 종종 그렇지 않습니다. 왜냐하면 이러한 호출 중 많은 부분이 코드를 처리하고 내용을 인라인하는 다양한 컴파일러 / 통역사에 의해 오버 헤드가 최적화 될 것입니다. 귀하의 언어에 이러한 종류의 최적화가 없으면 현대적으로 생각하지 않을 수 있습니다.
Ixrec

2
성능에 어떤 영향을 미칩니 까? 사용하는 특정 언어와 실제 코드의 구조 및 사용중인 컴파일러의 버전 및 사용중인 플랫폼에 따라 더 빠르거나 느리게 변경하거나 변경하지 않습니다. 다시 실행 중입니다. 당신이 얻는 모든 대답은 더 많은 단어와 더 많은 증거를 통해이 불확실성의 변형이 될 것입니다.
GrandOpener

1
영향은 어떤 경우, 사람이, 결코 너무 작 사상 을 알 수 없습니다. 걱정해야 할 훨씬 더 중요한 것들이 있습니다. 탭이 5 또는 7 공백이어야하는지 여부와 같습니다.
MetaFight

답변:


21

아마도. 컴파일러는 "이 함수는 몇 번만 호출되며 속도를 최적화해야하므로이 함수를 인라인 할 것"이라고 결정할 수 있습니다. 기본적으로 컴파일러는 함수 호출을 함수 본문으로 바꿉니다. 예를 들어 소스 코드는 다음과 같습니다.

void DoSomething()
{
   a = a + 1;
   DoSomethingElse(a);
}

void DoSomethingElse(int a)
{
   b = a + 3;
}

컴파일러는 inline을 결정 DoSomethingElse하고 코드는

void DoSomething()
{
   a = a + 1;
   b = a + 3;
}

함수가 인라인되지 않은 경우 함수 호출을 수행 할 때 성능이 저하됩니다. 그러나 매우 고성능 코드만이 함수 호출에 대해 걱정할 것입니다. 그리고 이러한 종류의 프로젝트에서 코드는 일반적으로 어셈블리로 작성됩니다.

함수 호출 (플랫폼에 따라 다름)에는 일반적으로 몇 가지 명령이 수반되며 스택 저장 / 복원을 포함합니다. 일부 함수 호출은 점프 및 리턴 명령으로 구성됩니다.

그러나 함수 호출 성능에 영향을 줄 수있는 다른 사항이 있습니다. 호출되는 함수가 프로세서의 캐시에로드되지 않아 캐시 미스가 발생하고 메모리 컨트롤러가 주 RAM에서 함수를 가져옵니다. 이로 인해 성능이 크게 저하 될 수 있습니다.

간단히 말해서 : 함수 호출은 성능에 영향을 줄 수도 있고받지 않을 수도 있습니다. 말할 수있는 유일한 방법은 코드를 프로파일 링하는 것입니다. 컴파일러와 하드웨어에는 소매에 놀라운 속임수가 있기 때문에 느린 코드 스팟이 어디에 있는지 추측하지 마십시오. 느린 지점의 위치를 ​​얻으려면 코드를 프로파일하십시오.


1
나는 큰 함수 내에서 루프에 대해 매우 나쁜 코드를 만들었을 때 현대 컴파일러 (gcc, clang)를 보았습니다 . 정적 함수로 루프를 추출하는 것은 인라인으로 인해 도움이되지 않았습니다. 루프를 외부 함수로 추출하여 경우에 따라 (벤치 마크에서 측정 가능) 속도가 크게 향상되었습니다.
gnasher729

1
나는이 다시-피기과 영업 이익은주의해야 말을 조기 최적화
패트릭

1
@ 패트릭 빙고. 최적화하려는 경우 프로파일 러를 사용하여 느린 섹션의 위치를 ​​봅니다. 추측하지 마십시오. 일반적으로 느린 섹션의 위치를 ​​느낄 수 있지만 프로파일 러로 확인하십시오.
CHendrix

@ gnasher729이 특정 문제를 해결하려면 프로파일 러 이상이 필요합니다. 분해 된 기계 코드도 읽는 법을 배워야합니다. 조기 최적화가 있지만 조기 학습 (적어도 소프트웨어 개발에서는) 과 같은 것은 없습니다 .
rwong

당신은 할 수 당신이 기능을 백만 번 호출하는 경우이 문제를 가지고 있지만, 당신은 훨씬 더 큰 영향을 미치고 다른 문제가있을 가능성이 더 높습니다.
Michael Shaw

5

이것은 컴파일러 또는 런타임 (및 옵션)의 구현 문제이므로 확실하게 말할 수는 없습니다.

C 및 C ++에서 일부 컴파일러는 최적화 설정에 따라 호출을 인라인합니다. https://gcc.godbolt.org/

Java와 같은 다른 언어는이를 런타임의 일부로 사용합니다. 이것은 JIT의 일부이며이 SO 질문 에서 자세히 설명합니다 . 특히 HotSpotJVM 옵션을 살펴보십시오.

-XX:InlineSmallCode=n 생성 된 고유 코드 크기가 이보다 작은 경우에만 이전에 컴파일 된 메소드를 인라인하십시오. 기본값은 JVM이 실행중인 플랫폼에 따라 다릅니다.
-XX:MaxInlineSize=35 인라인 될 메소드의 최대 바이트 코드 크기.
-XX:FreqInlineSize=n 인라인 될 자주 실행되는 메소드의 최대 바이트 코드 크기. 기본값은 JVM이 실행중인 플랫폼에 따라 다릅니다.

예, HotSpot JIT 컴파일러는 특정 기준에 맞는 메소드를 인라인합니다.

이것의 영향 은 각 JVM (또는 컴파일러)이 다르게 작동 할 수 있으며 언어의 광범위한 스트로크로 대답하려고 시도하는 것이 거의 확실하지 않기 때문에 결정하기 어렵습니다. 적절한 실행 환경에서 코드를 프로파일 링하고 컴파일 된 출력을 검사하여 영향을 제대로 판단 할 수 있습니다.

이는 CPython이 인라이닝되지 않지만 Jython (JVM에서 실행중인 Python)이 인라인 된 일부 잘못된 접근 방식으로 볼 수 있습니다. 마찬가지로 MRI Ruby는 JRuby와는 달리 인라인하지 않고 루비를 C로 변환하는 ruby2c입니다. 이는 컴파일 된 C 컴파일러 옵션에 따라 인라인되거나 아닐 수 있습니다.

언어는 인라인되지 않습니다. 구현이 있을 수 있습니다 .


5

잘못된 장소에서 성능을 찾고 있습니다. 함수 호출의 문제점은 비용이 많이 든다는 것이 아닙니다. 또 다른 문제가 있습니다. 함수 호출은 완전 무료 일 수 있으며 여전히 다른 문제가 있습니다.

기능은 신용 카드와 같습니다. 쉽게 사용할 수 있기 때문에 원하는 것보다 더 많이 사용하는 경향이 있습니다. 필요한 것보다 20 % 더 많이 호출한다고 가정하십시오. 그런 다음 일반적인 대형 소프트웨어에는 여러 계층이 포함되어 있으며 각 계층은 아래 계층에서 기능을 수행하므로 1.2의 계수는 계층 수에 따라 복잡해질 수 있습니다. 예를 들어 5 개의 계층이 있고 각 계층의 감속 계수가 1.2 인 경우 복합 감속 계수는 1.2 ^ 5 또는 2.5입니다. 이것은 생각할 수있는 한 가지 방법입니다.

함수 호출을 피해야한다는 의미는 아닙니다. 의미는 코드가 작동 할 때 낭비를 찾아서 제거하는 방법을 알아야한다는 것입니다. stackexchange 사이트 에서이 작업을 수행하는 방법에 대한 훌륭한 조언이 있습니다. 이것은 내 공헌 중 하나를 제공합니다.

추가 : 작은 예. 일단 공장에서 일련의 작업 지시 또는 "작업"을 추적하는 공장 플로어 소프트웨어에 대해 작업했습니다. JobDone(idJob)작업이 완료되었는지 알 수 있는 기능 이있었습니다. 모든 하위 작업이 완료되면 작업이 완료되었으며 모든 하위 작업이 완료되면 각 작업이 완료되었습니다. 이 모든 것들이 관계형 데이터베이스에서 추적되었습니다. 다른 함수를 한 번 호출하면 다른 함수 JobDone라고하는 모든 정보를 추출 하여 작업이 완료되었는지 확인하고 나머지는 버렸습니다. 그러면 사람들은 다음과 같은 코드를 쉽게 작성할 수 있습니다.

while(!JobDone(idJob)){
    ...
}

또는

foreach(idJob in jobs){
    if (JobDone(idJob)){
        ...
    }
}

요점을 참조하십시오? 이 기능은 너무 강력하고 호출하기 쉽기 때문에 매우 강력했습니다. 따라서 성능 문제는 기능으로 들어오고 나가는 명령이 아닙니다. 작업이 완료되었는지 알 수있는보다 직접적인 방법이 필요했습니다. 이 코드는 수천 줄의 다른 무고한 코드에 포함되었을 수 있습니다. 미리 고치려고하는 것은 모든 사람들이하려는 일이지만, 어두운 방에 다트를 던지는 것과 같습니다. 대신 필요한 것은 실행 한 다음 "느린 코드"가 단순히 시간을내어 그것이 무엇인지 알려주는 것입니다. 이를 위해 무작위 일시 중지를 사용 합니다.


1

언어와 기능에 따라 달라집니다. c 및 c ++ 컴파일러는 많은 함수를 인라인 할 수 있지만 Python 또는 Java의 경우에는 해당되지 않습니다.

Java에 대한 특정 세부 정보는 알지 못하지만 (모든 방법이 가상이지만 문서를 더 잘 확인하는 것이 좋습니다) 파이썬에서는 인라인이 없으며 꼬리 재귀 최적화가 없으며 함수 호출이 상당히 비쌉니다.

파이썬 함수는 기본적으로 실행 가능한 객체입니다 (실제로 객체 인스턴스를 함수로 만들기 위해 call () 메서드를 정의 할 수도 있습니다). 이것은 그것들을 호출하는 데 많은 오버 헤드가 있음을 의미합니다 ...

그러나

함수 내에 변수를 정의 할 때 인터프리터는 바이트 코드의 일반 LOAD 명령어 대신 LOADFAST를 사용하여 코드를 더 빠르게 만듭니다.

또 다른 것은 호출 가능한 객체를 정의 할 때 메모 화와 같은 패턴이 가능하고 더 많은 메모리를 사용하는 비용으로 계산 속도를 효과적으로 높일 수 있다는 것입니다. 기본적으로 항상 트레이드 오프입니다. 함수 호출 비용은 또한 스택에 실제로 복사 해야하는 양을 결정하기 때문에 매개 변수에 따라 다릅니다 (따라서 c / c ++에서는 큰 매개 변수를 값 대신 포인터 / 참조로 전달하는 것이 일반적입니다).

귀하의 질문이 실제로 너무 광범위하여 스택 교환에 완전히 대답 할 수 없다고 생각합니다.

내가 제안하는 것은 하나의 언어로 시작하고 고급 설명서를 연구하여 해당 언어로 함수 호출이 어떻게 구현되는지 이해하는 것입니다.

이 과정에서 얼마나 많은 것들을 배우게 될지 놀랄 것입니다.

특정 문제가있는 경우 측정 / 프로파일 링을 수행하고 날씨를 결정하면 함수를 작성하거나 동등한 코드를 복사 / 붙여 넣기하는 것이 좋습니다.

더 구체적인 질문을하면 더 구체적인 답변을 얻는 것이 더 쉬울 것입니다.


"귀하의 질문이 실제로 너무 광범위하여 스택 교환에 대해 완전히 대답 할 수 없다고 생각합니다." 그렇다면 어떻게 좁힐 수 있습니까? 함수 호출이 성능에 미치는 영향을 나타내는 실제 데이터를보고 싶습니다. 나는 어떤 언어를 신경 쓰지 않고, 가능한 한 데이터로 백업 된 더 자세한 설명을 보는 것이 궁금합니다.
dabadaba

요점은 언어에 달려 있다는 것입니다. C 및 C ++에서 함수가 인라인되면 영향은 0입니다. 인라인되지 않은 경우 캐시에 있는지 여부에 따라 매개 변수에 따라 달라집니다.
ingframin

1

얼마 전에 Xenon PowerPC에서 직접 및 가상 C ++ 함수 호출의 오버 헤드를 측정했습니다 .

해당 함수는 단일 매개 변수와 단일 리턴을 가지므로 레지스터에서 매개 변수 전달이 발생했습니다.

간단히 말해, 직접 (가상이 아닌) 함수 호출의 오버 헤드는 인라인 함수 호출과 비교하여 약 5.5 나노초 (18 클럭주기)였습니다. 가상 함수 호출의 오버 헤드는 인라인과 비교하여 13.2 나노초 (42 클럭주기)였습니다.

이러한 타이밍은 프로세서 제품군마다 다를 수 있습니다. 내 테스트 코드는 여기에 있습니다 . 하드웨어에서 동일한 실험을 실행할 수 있습니다. CFastTimer 구현에 rdtsc 와 같은 고정밀 타이머를 사용하십시오 . 시스템 시간 ()이 충분히 정확하지 않습니다.

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