메소드 또는 함수로 기능을 추출하는 것은 특히 OOP에서 코드 모듈화, 가독성 및 상호 운용성을 위해 반드시 필요합니다.
그러나 이것은 더 많은 함수 호출이 이루어질 것임을 의미합니다.
코드를 메소드 또는 함수로 분리하면 실제로 현대 언어의 성능에 어떤 영향을 미칩니 까?
* 가장 인기있는 것 : C, Java, C ++, C #, Python, JavaScript, Ruby ...
메소드 또는 함수로 기능을 추출하는 것은 특히 OOP에서 코드 모듈화, 가독성 및 상호 운용성을 위해 반드시 필요합니다.
그러나 이것은 더 많은 함수 호출이 이루어질 것임을 의미합니다.
코드를 메소드 또는 함수로 분리하면 실제로 현대 언어의 성능에 어떤 영향을 미칩니 까?
* 가장 인기있는 것 : C, Java, C ++, C #, Python, JavaScript, Ruby ...
답변:
아마도. 컴파일러는 "이 함수는 몇 번만 호출되며 속도를 최적화해야하므로이 함수를 인라인 할 것"이라고 결정할 수 있습니다. 기본적으로 컴파일러는 함수 호출을 함수 본문으로 바꿉니다. 예를 들어 소스 코드는 다음과 같습니다.
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에서 함수를 가져옵니다. 이로 인해 성능이 크게 저하 될 수 있습니다.
간단히 말해서 : 함수 호출은 성능에 영향을 줄 수도 있고받지 않을 수도 있습니다. 말할 수있는 유일한 방법은 코드를 프로파일 링하는 것입니다. 컴파일러와 하드웨어에는 소매에 놀라운 속임수가 있기 때문에 느린 코드 스팟이 어디에 있는지 추측하지 마십시오. 느린 지점의 위치를 얻으려면 코드를 프로파일하십시오.
이것은 컴파일러 또는 런타임 (및 옵션)의 구현 문제이므로 확실하게 말할 수는 없습니다.
C 및 C ++에서 일부 컴파일러는 최적화 설정에 따라 호출을 인라인합니다. https://gcc.godbolt.org/
Java와 같은 다른 언어는이를 런타임의 일부로 사용합니다. 이것은 JIT의 일부이며이 SO 질문 에서 자세히 설명합니다 . 특히 HotSpot 의 JVM 옵션을 살펴보십시오.
-XX:InlineSmallCode=n
생성 된 고유 코드 크기가 이보다 작은 경우에만 이전에 컴파일 된 메소드를 인라인하십시오. 기본값은 JVM이 실행중인 플랫폼에 따라 다릅니다.
-XX:MaxInlineSize=35
인라인 될 메소드의 최대 바이트 코드 크기.
-XX:FreqInlineSize=n
인라인 될 자주 실행되는 메소드의 최대 바이트 코드 크기. 기본값은 JVM이 실행중인 플랫폼에 따라 다릅니다.
예, HotSpot JIT 컴파일러는 특정 기준에 맞는 메소드를 인라인합니다.
이것의 영향 은 각 JVM (또는 컴파일러)이 다르게 작동 할 수 있으며 언어의 광범위한 스트로크로 대답하려고 시도하는 것이 거의 확실하지 않기 때문에 결정하기 어렵습니다. 적절한 실행 환경에서 코드를 프로파일 링하고 컴파일 된 출력을 검사하여 영향을 제대로 판단 할 수 있습니다.
이는 CPython이 인라이닝되지 않지만 Jython (JVM에서 실행중인 Python)이 인라인 된 일부 잘못된 접근 방식으로 볼 수 있습니다. 마찬가지로 MRI Ruby는 JRuby와는 달리 인라인하지 않고 루비를 C로 변환하는 ruby2c입니다. 이는 컴파일 된 C 컴파일러 옵션에 따라 인라인되거나 아닐 수 있습니다.
언어는 인라인되지 않습니다. 구현이 있을 수 있습니다 .
잘못된 장소에서 성능을 찾고 있습니다. 함수 호출의 문제점은 비용이 많이 든다는 것이 아닙니다. 또 다른 문제가 있습니다. 함수 호출은 완전 무료 일 수 있으며 여전히 다른 문제가 있습니다.
기능은 신용 카드와 같습니다. 쉽게 사용할 수 있기 때문에 원하는 것보다 더 많이 사용하는 경향이 있습니다. 필요한 것보다 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)){
...
}
}
요점을 참조하십시오? 이 기능은 너무 강력하고 호출하기 쉽기 때문에 매우 강력했습니다. 따라서 성능 문제는 기능으로 들어오고 나가는 명령이 아닙니다. 작업이 완료되었는지 알 수있는보다 직접적인 방법이 필요했습니다. 이 코드는 수천 줄의 다른 무고한 코드에 포함되었을 수 있습니다. 미리 고치려고하는 것은 모든 사람들이하려는 일이지만, 어두운 방에 다트를 던지는 것과 같습니다. 대신 필요한 것은 실행 한 다음 "느린 코드"가 단순히 시간을내어 그것이 무엇인지 알려주는 것입니다. 이를 위해 무작위 일시 중지를 사용 합니다.
언어와 기능에 따라 달라집니다. c 및 c ++ 컴파일러는 많은 함수를 인라인 할 수 있지만 Python 또는 Java의 경우에는 해당되지 않습니다.
Java에 대한 특정 세부 정보는 알지 못하지만 (모든 방법이 가상이지만 문서를 더 잘 확인하는 것이 좋습니다) 파이썬에서는 인라인이 없으며 꼬리 재귀 최적화가 없으며 함수 호출이 상당히 비쌉니다.
파이썬 함수는 기본적으로 실행 가능한 객체입니다 (실제로 객체 인스턴스를 함수로 만들기 위해 call () 메서드를 정의 할 수도 있습니다). 이것은 그것들을 호출하는 데 많은 오버 헤드가 있음을 의미합니다 ...
그러나
함수 내에 변수를 정의 할 때 인터프리터는 바이트 코드의 일반 LOAD 명령어 대신 LOADFAST를 사용하여 코드를 더 빠르게 만듭니다.
또 다른 것은 호출 가능한 객체를 정의 할 때 메모 화와 같은 패턴이 가능하고 더 많은 메모리를 사용하는 비용으로 계산 속도를 효과적으로 높일 수 있다는 것입니다. 기본적으로 항상 트레이드 오프입니다. 함수 호출 비용은 또한 스택에 실제로 복사 해야하는 양을 결정하기 때문에 매개 변수에 따라 다릅니다 (따라서 c / c ++에서는 큰 매개 변수를 값 대신 포인터 / 참조로 전달하는 것이 일반적입니다).
귀하의 질문이 실제로 너무 광범위하여 스택 교환에 완전히 대답 할 수 없다고 생각합니다.
내가 제안하는 것은 하나의 언어로 시작하고 고급 설명서를 연구하여 해당 언어로 함수 호출이 어떻게 구현되는지 이해하는 것입니다.
이 과정에서 얼마나 많은 것들을 배우게 될지 놀랄 것입니다.
특정 문제가있는 경우 측정 / 프로파일 링을 수행하고 날씨를 결정하면 함수를 작성하거나 동등한 코드를 복사 / 붙여 넣기하는 것이 좋습니다.
더 구체적인 질문을하면 더 구체적인 답변을 얻는 것이 더 쉬울 것입니다.
얼마 전에 Xenon PowerPC에서 직접 및 가상 C ++ 함수 호출의 오버 헤드를 측정했습니다 .
해당 함수는 단일 매개 변수와 단일 리턴을 가지므로 레지스터에서 매개 변수 전달이 발생했습니다.
간단히 말해, 직접 (가상이 아닌) 함수 호출의 오버 헤드는 인라인 함수 호출과 비교하여 약 5.5 나노초 (18 클럭주기)였습니다. 가상 함수 호출의 오버 헤드는 인라인과 비교하여 13.2 나노초 (42 클럭주기)였습니다.
이러한 타이밍은 프로세서 제품군마다 다를 수 있습니다. 내 테스트 코드는 여기에 있습니다 . 하드웨어에서 동일한 실험을 실행할 수 있습니다. CFastTimer 구현에 rdtsc 와 같은 고정밀 타이머를 사용하십시오 . 시스템 시간 ()이 충분히 정확하지 않습니다.