IT이야기

최적화가 이 기능을 중지시키는 이유는 무엇입니까?

cyworld 2022. 6. 8. 23:43
반응형

최적화가 이 기능을 중지시키는 이유는 무엇입니까?

우리는 최근 대학에서 여러 언어의 프로그래밍 특강에 대한 강의를 했다.

강사는 다음과 같은 기능을 적었다.

inline u64 Swap_64(u64 x)
{
    u64 tmp;
    (*(u32*)&tmp)       = Swap_32(*(((u32*)&x)+1));
    (*(((u32*)&tmp)+1)) = Swap_32(*(u32*) &x);

    return tmp;
}

가독성 면에서도 매우 나쁜 스타일인 것은 잘 알고 있습니다만, 코드의 이 부분은 높은 최적화 레벨이 될 때까지 프로덕션 코드로 정상적으로 동작하고 있었습니다.그러면, 그 코드는 아무 것도 하지 않습니다.

는 변수에 이 다 .tmp컴파일러에 의해 최적화됩니다.지만만 왜런 ?이? ????

컴파일러가 읽거나 쓰지 않았다고 생각하더라도 변수를 건드리지 않기 위해 변수를 휘발성이라고 선언해야 하는 상황은 이해하지만, 여기서 왜 이런 일이 일어나는지 모르겠습니다.

에일리어스가 아닌 됩니다(C++는 제외).char*에일리어스 규칙)이 근본적으로 다른 유형을 가리키고 있는 경우.이것에 의해, 몇개의 최적화가 가능하게 됩니다.

서서,,u64 tmp는 절대 .u64.
★★★의 u32* '이러다'와는 이 없을 수 .u64 tmp는 이렇게 볼 수 .nop★★★★★★에u64 tmp.

이 코드는 *char **를 통한 액세스는 허용되지만 다른 유형의 포인터를 통해 개체에 액세스하는 것을 불법으로 하는 엄격한 에일리어스 규칙을 위반합니다.컴파일러는 다른 타입의 포인터가 같은 메모리를 가리키지 않는다고 가정하고 그에 따라 최적화할 수 있습니다.또한 코드가 정의되지 않은 동작을 호출하여 실제로 어떤 작업도 수행할 수 있음을 의미합니다.

이 토픽의 가장 좋은 레퍼런스 중 하나는 엄밀한 에일리어싱의 이해입니다.첫 번째 예는 OP 코드와 같은 맥락입니다.

uint32_t swap_words( uint32_t arg )
{
  uint16_t* const sp = (uint16_t*)&arg;
  uint16_t        hi = sp[0];
  uint16_t        lo = sp[1];

  sp[1] = hi;
  sp[0] = lo;

 return (arg);
} 

기사에서는 이 코드가 엄격한 에일리어스 규칙을 위반하고 있다고 설명하고 있습니다.sp입니다.arg 종류도 아마 것, 라고 말하고 .arg 후에도 변하지 않습니다.swap_words돌아온다.간단한 테스트에서는 위의 코드나 OPS 코드 중 하나로 결과를 재현할 수 없지만 이는 정의되지 않은 동작이기 때문에 예측할 수 없기 때문에 의미가 없습니다.

이 기사에서는 많은 다양한 사례에 대해 설명하고 있으며, C99에서 1 정의되어 있고 C++에서 정의되어 있지 않지만 실제로는 대부분의 주요 컴파일러에서 지원되는 유형 패닝에 대한 gcc의 참조를 소개합니다.C와 C++에서 유니언의 이전 줄거리는 뼈아픈 세부사항으로 들어간다.이 주제에 대해서는 여러 가지 설이 있지만, 이것이 가장 효과가 있는 것 같습니다.

이 솔루션의 코드는 다음과 같습니다.

typedef union
{
  uint32_t u32;
  uint16_t u16[2];
} U32;

uint32_t swap_words( uint32_t arg )
{
  U32      in;
  uint16_t lo;
  uint16_t hi;

  in.u32    = arg;
  hi        = in.u16[0];
  lo        = in.u16[1];
  in.u16[0] = lo;
  in.u16[1] = hi;

  return (in.u32);
}

참고엄격한 에일리어싱에 관한 C99 초안 표준의 관련 섹션은 다음과 같다.6.5 제7항표현은 다음과 같다.

오브젝트는 다음 76)중 하나의 유형의 lvalue 식을 통해서만 저장된 값에 액세스할 수 있어야 합니다.

: 객체의 유효한 유형과 호환되는 유형.

: 객체의 유효 유형과 호환되는 유형의 정규 버전.

: 객체의 유효한 유형에 대응하는 부호 있는 유형 또는 부호 없는 유형입니다.

: 오브젝트의 유효한 유형의 정규 버전에 대응하는 서명된 유형 또는 서명되지 않은 유형입니다.

- 구성원 중 앞서 언급한 유형 중 하나를 포함하는 집합 또는 결합 유형(반복적으로 하위 집합 또는 포함된 결합의 구성원 포함) 또는

: 문자 타입.

및 각주 76은 다음과 같이 기술되어 있습니다.

이 목록의 목적은 객체가 에일리어스 될 수도 있고 그렇지 않을 수도 있습니다.

C++ 초안 기준의 관련 섹션은3.10 Lvalues and rvalues

Type-punning and strict-aliasing이라는 기사는 이 주제에 대해 좀 더 온화하지만 덜 완전한 소개를 제공하며, C99는 C99와 에일리어싱에 대한 깊은 분석을 제공하며, 쉽게 읽을 수 있는 것은 아니다.비활성 유니언 멤버 액세스에 대한 이 답변 - 정의되지 않았습니까?C++의 유니언을 통해 활자 패닝의 진부한 디테일을 살펴보고 가벼운 판독도 아닙니다.


각주:

  1. Pascal Cuoq의 코멘트를 인용:[...C99] 처음에는 어설프게 표현되어 결합을 통한 활자 조작을 정의하지 않은 것으로 보인다. 실제로는 조합이 C89에서 합법이고, C11에서 합법이며, 위원회가 잘못된 표현을 수정하는 데 2004년까지 걸렸으나, 이후 TC3가 출시될 때까지 C99에서 합법이었다. open-std.org/jtc1/sc22/wg14/www/docs/dr_283.htm

g++(Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1:

> g++ -Wall -std=c++11 -O0 -o sample sample.cpp

> g++ -Wall -std=c++11 -O3 -o sample sample.cpp
sample.cpp: In function ‘uint64_t Swap_64(uint64_t)’:
sample.cpp:10:19: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
     (*(uint32_t*)&tmp)       = Swap_32(*(((uint32_t*)&x)+1));
                   ^
sample.cpp:11:54: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
     (*(((uint32_t*)&tmp)+1)) = Swap_32(*(uint32_t*) &x);
                                                      ^

Clang 3.4는 어떤 최적화 수준에서도 경고하지 않습니다. 이상하게도...

언급URL : https://stackoverflow.com/questions/20922609/why-does-optimisation-kill-this-function

반응형