Clang은 왜 x * 1.0을 최적화하지만 x + 0.0은 최적화하지 않습니까?


125

Clang이이 코드에서 루프를 최적화하는 이유

#include <time.h>
#include <stdio.h>

static size_t const N = 1 << 27;
static double arr[N] = { /* initialize to zero */ };

int main()
{
    clock_t const start = clock();
    for (int i = 0; i < N; ++i) { arr[i] *= 1.0; }
    printf("%u ms\n", (unsigned)(clock() - start) * 1000 / CLOCKS_PER_SEC);
}

그러나이 코드의 루프는 아닙니까?

#include <time.h>
#include <stdio.h>

static size_t const N = 1 << 27;
static double arr[N] = { /* initialize to zero */ };

int main()
{
    clock_t const start = clock();
    for (int i = 0; i < N; ++i) { arr[i] += 0.0; }
    printf("%u ms\n", (unsigned)(clock() - start) * 1000 / CLOCKS_PER_SEC);
}

(응답이 각각 다른지 알고 싶기 때문에 C와 C ++로 태깅합니다.)


2
현재 어떤 최적화 플래그가 활성화되어 있습니까?
Idonotexist Idonotexist

1
@IwillnotexistIdonotexist : 방금 사용 -O3했지만 활성화 된 것을 확인하는 방법을 모르겠습니다.
user541686

2
명령 행에 -ffast-math를 추가하면 어떤 일이 발생하는지 보는 것이 흥미로울 것입니다.
plugwash

static double arr[N]C에서는 허용되지 않습니다. const변수는 해당 언어의 상수 표현으로 간주되지 않습니다
MM

1
[당신이 이미 그것을 불러 냈지만 C가 C ++이 아닌 방법에 대한 엉뚱한 의견을 삽입하십시오.]
user253751

답변:


164

부동 소수점 산술을위한 IEEE 754-2008 표준 및 ISO / IEC 10967 LIA (Language Independent Arithmetic) 표준, 1 부는 이것이 왜 그런지에 대한 답변입니다.

IEEE 754 § 6.3 부호 비트

입력 또는 결과가 NaN 인 경우이 표준은 NaN의 부호를 해석하지 않습니다. 그러나 비트 문자열 (copy, negate, abs, copySign)에 대한 작업은 NaN 피연산자의 부호 비트에 따라 NaN 결과의 부호 비트를 지정합니다. 논리 술어 totalOrder도 NaN 피연산자의 부호 비트의 영향을받습니다. 다른 모든 작업의 ​​경우이 표준은 입력 NaN이 하나만 있거나 유효하지 않은 작업으로 NaN이 생성 된 경우에도 NaN 결과의 부호 비트를 지정하지 않습니다.

입력 값이나 결과가 NaN이 아닌 경우 곱 또는 부호의 부호는 피연산자 부호의 배타적 OR입니다. 합의 부호 또는 합 x + (-y)로 간주되는 차이 x-y는 최대 부수 부호와 다릅니다. 변환 결과의 부호, 양자화 연산, roundTo-Integral 연산 및 roundToIntegralExact (5.3.1 참조)는 첫 번째 또는 유일한 피연산자의 부호입니다. 이 규칙은 피연산자 또는 결과가 0 또는 무한 인 경우에도 적용됩니다.

반대 부호를 가진 두 피연산자의 합 (또는 같은 부호를 가진 두 피연산자의 차이)이 정확히 0 인 경우 roundTowardNegative를 제외한 모든 반올림 방향 속성에서 해당 합 (또는 차이)의 부호는 +0이어야합니다. 이 속성 하에서, 정확한 제로섬 (또는 차이)의 부호는 -0이어야한다. 그러나 x가 0 인 경우에도 x + x = x − (−x)는 x와 동일한 부호를 유지합니다.

덧셈의 ​​경우

기본 반올림 모드 (라운드에서 가장 가까운, 타이 에서 에븐까지 )에서 다음과 같은 경우를 제외 하고 x+0.0생성합니다 . 이 추가로 생성되는 3 가지 규칙 .xx-0.0+0.0

이후이 +0.0아닌 비트 본래 동일 -0.0하고, 그-0.0 입력으로 발생할 수있는 정상적인 값은 컴파일러에 부정적 제로 변환 될 코드에 배치해야만한다 +0.0.

요약 : 기본 아래에서, 반올림 모드 x+0.0경우,x

  • 하지 -0.0 후,x 자체는 허용 가능한 출력 값입니다.
  • 이면 -0.0 출력 값 +0.0 비트 단위와 동일하지 않아야합니다 -0.0.

곱셈의 경우

기본 반올림 모드 에서는 이러한 문제가 발생하지 않습니다 x*1.0. 만약x :

  • (하) 정수, x*1.0 == x항상 입니다.
  • 이다 +/- infinity 결과는 +/- infinity같은 부호입니다.
  • 이다 NaN 다음에있어서,

    IEEE 754 § 6.2.3 NaN 전파

    NaN 피연산자를 결과로 전파하고 입력으로 단일 NaN을 갖는 연산은 대상 형식으로 표현 가능한 경우 입력 NaN의 페이로드를 사용하여 NaN을 생성해야합니다.

    그중, 지수 및 가수 (아니지만 부호) 것을 의미 NaN*1.0되어 추천이 입력으로부터 변경 될 NaN. 위의 §6.3p1에 따라 부호가 지정되어 있지 않지만 구현시 소스와 동일하게 지정할 수 있습니다.NaN .

  • 이며 +/- 0.0, 결과는이다 0의 기호가의 부호 비트와 XOR 연산 비트 1.0§6.3p2와 일치. 의 부호 비트는 1.0이므로 0출력 값은 입력에서 변경되지 않습니다. 따라서 (음수) 0 인 x*1.0 == x경우에도 마찬가지 x입니다.

빼기의 경우

기본 반올림 모드 에서 뺄셈 x-0.0도와 동일하므로 뺄셈 도 작동하지 않습니다 x + (-0.0). 만약 x

  • NaN§6.3p1 및 §6.2.3이 덧셈 및 곱셈과 거의 같은 방식으로 적용됩니다.
  • 이면+/- infinity 결과는 +/- infinity같은 부호입니다.
  • x-0.0 == x항상 (하) 정수 입니다.
  • -0.0"다음 §6.3p2 의해 우리가, ...], 또는 차 X의 합계의 부호 - 간주 Y 합계 X + (-y) 상기 가수 '기호 많아야 하나 다르다; ". 이 힘을 우리는 할당 -0.0의 결과로 (-0.0) + (-0.0)인해 -0.0에서 로그인 다르다 없음 가산 수의, 동안 +0.0의 기호에 다릅니다 이 조항의 위반에 가수의.
  • 이고 +0.0, 이것은 가산 경우에 감소 (+0.0) + (-0.0)위에서 고려 첨가 케이스 §6.3p3 의해 수득 지배된다 +0.0.

모든 경우에 입력 값이 출력으로 합법적이기 때문에 x-0.0no-op 및 x == x-0.0타우 톨로지를 고려할 수 있습니다.

가치 변화 최적화

IEEE 754-2008 표준에는 다음과 같은 흥미로운 인용문이 있습니다.

IEEE 754 § 10.4 리터럴 의미 및 가치 변경 최적화

[...]

다음과 같은 값 변경 변환은 소스 코드의 문자 적 ​​의미를 유지합니다.

  • x가 0이 아니고 신호 NaN이 아니고 결과가 x와 동일한 지수를 갖는 경우 항등 속성 0 + x를 적용합니다.
  • x가 신호 NaN이 아니고 결과가 x와 동일한 지수를 갖는 경우 항등 성 1 × x를 적용합니다.
  • 조용한 NaN의 페이로드 또는 부호 비트 변경
  • [...]

모든 NaN이 모든 무한이 같은 지수, 그리고 올바르게 둥근 결과를 공유하기 때문에 x+0.0x*1.0유한에 대해 x정확히 같은 크기 등이있다x , 그들의 지수는 동일합니다.

sNaNs

시그널링 NaN은 부동 소수점 트랩 값입니다. 부동 소수점 피연산자로 사용하면 SIGFPE (잘못된 연산 예외)가 발생하는 특수 NaN 값입니다. 예외를 트리거하는 루프가 최적화 된 경우 소프트웨어는 더 이상 동일하게 동작하지 않습니다.

그러나 user2357112 가 주석에서 지적한 바와 같이 C11 표준은 NaN 신호의 동작을 정의되지 않은 채로 둡니다 (sNaN ) 므로 컴파일러는 발생하지 않는다고 가정 할 수 있으므로 발생하는 예외도 발생하지 않습니다. C ++ 11 표준은 NaN 신호에 대한 동작을 설명하지 않으므로 정의되지 않은 상태로 둡니다.

반올림 모드

대체 라운딩 모드에서는 허용되는 최적화가 변경 될 수 있습니다. 예를 들어, Round-to-Negative-Infinity 모드에서는 최적화 x+0.0 -> x가 허용되지만x-0.0 -> x 금지됩니다.

GCC가 기본 반올림 모드와 동작을 가정하지 않도록 실험 플래그 -frounding-math를 GCC로 전달할 수 있습니다.

결론

에 있더라도 Clang 및 GCC-O3 는 IEEE-754를 준수합니다. 이는 IEEE-754 표준의 위 규칙을 준수해야 함을 의미합니다. x+0.0비트와 동일하지x모두 x그 규칙 하에서하지만 x*1.0 그렇게되도록 선택 될 수있다 : 때, 즉

  1. xNaN 인 경우 페이로드를 변경하지 말 것을 권장합니다 .
  2. NaN 결과의 부호 비트는 다음과 같이 변경하지 마십시오. * 1.0 .
  3. NaN x아닌 경우 몫 / 제품 중에 부호 비트를 XOR하는 순서를 따르십시오 .

IEEE-754 안전하지 않은 최적화 (x+0.0) -> x를 사용하려면 플래그 -ffast-math를 Clang 또는 GCC에 전달해야합니다.


2
주의 사항 : 신호 NaN 인 경우 어떻게해야합니까? (실제로 그 이유가 될 수 있다고 생각했지만 실제로 방법을 몰랐기 때문에 물었습니다.)
user541686

6
@Mehrdad : IEEE 754에 대한 C 준수를 지정하는 C 표준의 (선택적) 부분 인 Annex F는 신호 NaN을 명시 적으로 다루지 않습니다. (C11 F.2.1., 첫 번째 행 : "이 규격은 NaN 신호의 동작을 정의하지 않습니다.") Annex F에 대한 적합성을 선언하는 구현은 NaN 신호로 원하는 것을 자유롭게 수행 할 수 있습니다. C ++ 표준에는 IEEE 754에 대한 자체 처리 기능이 있지만 그것이 무엇이든간에 (나도 익숙하지는 않지만) NaN 동작을 신호 화하는 것으로 의심됩니다.
user2357112는

2
@Mehrdad : sNaN은 표준에 따라 정의되지 않은 동작을 호출하지만 (플랫폼에서 잘 정의되어 있음) 여기에서 컴파일러 스 쿼싱이 허용됩니다.
Joshua

1
@ user2357112 : 사용되지 않은 계산에 대한 부작용으로 오류 트래핑의 가능성은 일반적으로 많은 최적화를 방해합니다. 계산 결과가 때때로 무시되는 경우, 컴파일러는 결과의 사용 여부를 알 수있을 때까지 계산을 유용하게 연기 할 수 있지만, 계산에서 중요한 신호를 생성하면 나쁠 수 있습니다.
supercat

2
오, 봐, 하나의 표준에 대한 참조로 언어 모두에 정확하게 대답되는 C와 C ++ 모두에 합법적으로 적용되는 질문 . 이렇게하면 질문이 언어 공통성을 다루더라도 C와 C ++로 태그가 지정된 질문에 대해 사람들이 불평 할 가능성이 줄어 듭니까? 슬프게도, 나는 생각하지 않습니다.
Kyle Strand

35

x += 0.0경우 NOOP 아니다 x됩니다 -0.0. 그래도 결과가 사용되지 않으므로 최적화 프로그램이 전체 루프를 제거 할 수 있습니다. 일반적으로 옵티마이 저가 결정을 내리는 이유를 말하기는 어렵습니다.


2
나는 한 후에 사실이 게시 단지 이유를 읽어 x += 0.0아무 조합하지 않습니다, 그러나 나는 전체 루프 어느 방법을 최적화해야하기 때문에 그 아마 이유없는 생각했다. 내가 그것을 구입할 수 있습니다, 그것은 내가 기대했던 것만 큼 설득력이 없습니다 ...
user541686

객체 지향 언어가 부작용을 일으키는 경향이 있으므로 최적화 프로그램이 실제 동작을 변경하지 않는 것이 어렵다고 생각합니다.
Robert Harvey

long long최적화가 유효 하기 때문에 이유가 될 수 있습니다 ( 최소한 두 배로 동일하게 작동하는 gcc를 사용 했습니까 )
e2-e4

2
@ ringø : long long는 IEEE754 유형이 아닌 정수 유형입니다.
MSalters

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