programing

C와 C++ 옵티마이저는 일반적으로 어떤 함수가 부작용이 없는지 알고 있습니까?

javamemo 2023. 6. 14. 21:38
반응형

C와 C++ 옵티마이저는 일반적으로 어떤 함수가 부작용이 없는지 알고 있습니까?

sin, cos 등과 같은 매우 일반적인 수학 함수를 예로 들 수 있습니다.컴파일러는 부작용이 없고 외부 루프로 이동할 수 있다는 것을 알고 있습니까?예를들면

// Unoptimized

double YSinX(double x,int y)
{
   double total = 0.0;
   for (int i = 0; i < y; i++)
      total += sin(x);
   return total;
}

// Manually optimized

double YSinX(double x,int y)
{
   double total = 0.0, sinx = sin(x);
   for (int i = 0; i < y; i++)
      total += sinx;
   return total;
}

만약 그들이 할 수 있다면, 기능을 부작용이 없는 것으로 선언하고, 따라서 이러한 방식으로 최적화하는 것이 안전한 방법이 있습니까?VS2010 애플리케이션의 초기 프로파일링은 최적화가 유익하다는 것을 시사합니다.

이와 관련된 질문을 참조하십시오. 이 질문은 가깝지만 제가 직접 대답하지는 않습니다.

편집: 몇 가지 훌륭한 답변입니다.제가 받아들인 것은 답변 자체만큼이나 자극적인 댓글, 특히 링크된 기사, 그리고 리프팅이 발생하지 않을 수 있다는 사실에 근거했습니다.errno설정됩니다(즉, 부작용).이와 같이, 그리고 제가 하고 있는 일의 맥락에서, 이러한 유형의 수동 최적화는 여전히 이치에 맞는 것으로 보입니다.

GCC에는 두 가지 속성이 있습니다.pure그리고.const이러한 기능을 표시하는 데 사용할 수 있습니다. 그 가 오직한다면, 는 함에부이없함결인과의수가경합우함니다선수야언해를존는수하만에작수용고의▁should▁declared▁be▁if합다▁function니▁has▁the,야▁the함▁depends▁function선언해▁only-수▁its▁on함▁and▁its수▁no로 선언되어야 합니다.const도 있다면, 는 결가일글변의도존수할있합경니선다함야언해수를우과는수에부로벌▁should▁declared▁be▁if합다니▁function▁the▁variable▁the야결▁may▁results▁global▁depend선으로 선언되어야 합니다.pure최신 버전에는 다음과 같은 기능도 있습니다.-Wsuggest-attribute 선언해야 하는 함수를 가리킬 수 있는 경고 옵션const또는pure.

사실, 오늘날 일반적인 컴파일러는 당신이 요구하는 루프 불변 코드 모션 최적화를 수행할 것입니다.이에 대한 데모를 보려면 이 문서의 "최적화하시겠습니까?"라는 제목의 두 번째 연습을 참조하거나 다음을 사용합니다.gcc -S -O3 및/는clang -S -O3의 예제를 합니다.main내가 호기심 때문에 그랬던 것처럼, 어셈블리의 진입점.VS2010 컴파일러가 이 최적화를 수행하지 않는 경우, 중요하지 않습니다. llvm/clang은 "MSVC 2010, 2012, 201314 CTP와 통합"됩니다.

이론적 관점에서, 이 두 인용문은 컴파일러가 최적화를 수행할 때 갖는 범위 또는 헤드룸을 설명합니다.그것들은 C11 표준에서 왔습니다.IIRC C++11에도 비슷한 것이 있습니다.

◦5.1.2.3p4:

추상 시스템에서 모든 표현식은 의미론에 의해 지정된 대로 평가됩니다.실제 구현은 값이 사용되지 않고 필요한 부작용(함수 호출 또는 휘발성 객체 액세스로 인한 부작용 포함)이 발생하지 않는다고 추론할 수 있는 경우 표현식의 일부를 평가할 필요가 없습니다.

◦5.1.2.3p6:

적합한 구현에 대한 최소 요구사항은 다음과 같습니다.

휘발성 개체에 대한 액세스는 추상 시스템의 규칙에 따라 엄격하게 평가됩니다.

프로그램 종료 시, 파일에 기록된 모든 데이터는 추상적 의미론에 따른 프로그램 실행 결과와 동일해야 합니다.

대화형 장치의 입력 및 출력 역학은 7.21.3에 명시된 대로 발생해야 합니다.이러한 요구 사항의 목적은 버퍼링되지 않은 출력 또는 라인 버퍼링된 출력이 가능한 한 빨리 나타나 프로그램이 입력을 대기하기 전에 실제로 메시지가 나타나도록 하는 것입니다.

이것은 프로그램의 관찰 가능한 동작입니다.

따라서 컴파일러가 전체 프로그램을 컴파일 시간 평가로 끌어올릴 수 있습니다.다음 프로그램을 예로 들어 보겠습니다.

#include <math.h>
#include <stdio.h>

double YSinX(double x,int y)
{
    double total = 0.0;
    for (int i = 0; i < y; i++)
        total += sin(x);
    return total;
}

int main(void) {
    printf("%f\n", YSinX(M_PI, 4));
}

컴파일러가 이 프로그램이 인쇄된다는 것을 인식할 수 있습니다.0.0\n프로그램을 다음과 같이 최적화할 수 있습니다.

int main(void) { puts("0.0"); }

, 컴파일러를 제공하는 것은 어느 것도 증명할 수 없습니다.sin도 아니다YsinX부작용을 일으킬 수 있습니다.부작용이 여전히 발생할 수 있지만 이 프로그램의 출력을 생성하는 데는 부작용이 필요하지 않습니다.

실제 적용된 이론적 지식을 입증하기 위해 두 가지를 모두 테스트했습니다.llvm/clang(3.8.0 기준)clang --version및 from ) 및 gcc(6.4.0 준)gcc --version을하여 (용)을 사용하여 사용gcc -S -O3/clang -S -O3Windows 10 시스템에서 위의 코드를 사용하면 에 설명된 최적화 기능을 실제로 적용할 수 있습니다.main위의 예에서 다음과 같은 기계 코드로 변환됩니다.int main(void) { printf("%f", 0.0); }.

"컴파일러"에 대한 질문을 하셨습니다.만약 당신이 모든 C 또는 C++ 구현을 언급하고 있다면, 보장된 최적화는 없으며 C 구현은 컴파일러일 필요도 없습니다.위에서 설명했듯이 LLVM/Clang은 "MSVC 2010, 2012, 2013 및 14 CTP와 통합"되므로 어떤 특정 C 또는 C++ 구현을 사용할지도 모릅니다.C 또는 C++ 컴파일러가 최적의 코드를 생성하지 못하면 새로운 컴파일러(예: LLVM/Clang)를 구하거나 직접 최적화를 생성합니다. 가능하면 개발자에게 패치를 보낼 수 있도록 컴파일러를 수정하여 최적화를 다른 프로젝트에 자동으로 전파할 수 있습니다.

루프 외부에서 이 하위 표현식을 들어올리는 것을 허용하기 위해 필요한 것은 순수성이 아니라 동일한 능력입니다.

idempotence는 함수가 동일한 인수로 여러 번 호출되는 것처럼 한 번 호출되면 같은 부작용과 결과가 발생한다는 것을 의미합니다.따라서 컴파일러는 조건부로만 보호되는 함수 호출을 루프 외부에 배치할 수 있습니다(루프가 적어도 한 번 반복됩니까?).호이스트 최적화 후 실제 코드는 다음과 같습니다.

double YSinX(double x,int y)
{
   double total = 0.0;
   int i = 0;
   if (i < y) {
       double sinx = sin(x);  // <- this goes between the loop-initialization
                              // first test of the condition expression
                              // and the loop body
       do {
          total += sinx;
          i++;
       } while (i < y);
   }
   return total;
}

사이의 차이__attribute__(pure)그리고.idempotent그의 논평에서 아델이 언급했듯이, 이러한 기능은 설정의 부작용을 가지고 있기 때문에 중요합니다.errno.

그러나 동일성은 개입 지침 없이 반복적인 통화에만 적용되므로 주의해야 합니다.컴파일러는 함수와 개입 코드가 상호 작용하지 않는다는 것을 증명하기 위해 데이터 흐름 분석을 수행해야 합니다(예: 개입 코드는 주소를 가져가지 않는 로컬만 사용).함수가 순수한 것으로 알려진 경우에는 필요하지 않습니다.하지만 순도는 많은 기능에 적용되지 않는 훨씬 더 강한 조건입니다.

그런 것 같아요.컴파일러 분해 출력을 받으면 sin이 'for'에 대한 루프 레이블이 아닌 다른 레이블에서 호출됨을 알 수 있습니다. (g++ -O1 -O2 -O3로 컴파일됨)

Leh_func_begin1:
        pushq   %rbp
Ltmp0:
        movq    %rsp, %rbp
Ltmp1:
        pushq   %rbx
        subq    $8, %rsp
Ltmp2:
        testl   %edi, %edi
        jg      LBB1_2
        pxor    %xmm1, %xmm1
        jmp     LBB1_4
LBB1_2:
        movl    %edi, %ebx
        callq   _sin ;sin calculated
        pxor    %xmm1, %xmm1
        .align  4, 0x90
LBB1_3:
        addsd   %xmm0, %xmm1
        decl    %ebx
        jne     LBB1_3 ;loops here till i reaches y
LBB1_4:
        movapd  %xmm1, %xmm0

제 말이 맞았으면 좋겠네요.

언급URL : https://stackoverflow.com/questions/16233497/do-c-and-c-optimizers-typically-know-which-functions-have-no-side-effects

반응형