Clang은 x * 1.0을 최적화하지만 x + 0.0은 최적화하지 않는 이유는 무엇입니까?
왜 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++로 태그 붙입니다.)
IEEE 754-2008 부동소수점 산술 표준 및 ISO/IEC 10967 언어 독립 산술(LIA) 표준, Part 1은 그 이유를 설명합니다.
IEEE 754 © 6.3 부호 비트
입력 또는 결과 중 하나가 NaN인 경우, 이 표준은 NaN의 부호를 해석하지 않는다.단, 비트 문자열(copy, negate, abs, copySign)에 대한 연산은 NaN 결과의 부호 비트를 지정합니다.경우에 따라서는 NaN 오퍼랜드의 부호 비트를 기반으로 합니다.논리 술어 totalOrder도 NaN 오퍼랜드의 부호 비트의 영향을 받습니다.기타 모든 동작의 경우 입력 NaN이 1개뿐일 때 또는 무효 동작에서 NaN이 생성되는 경우에도 이 표준은 NaN 결과의 부호 비트를 규정하지 않는다.
입력값과 결과가 모두 NaN이 아닌 경우, 제품 또는 지수의 부호는 피연산자 부호의 배타적 논리합이며, 합 또는 차이의 부호는 합 x +(-y)로 간주되며, 변환 결과의 부호는 최대 하나의 부호와 다르다. 그리고 변환, 양자화 연산, 반올림 연산, 반올림 연산 및 연산을 한다.roundToIntegalExact(5.3.1 참조)는 첫 번째 또는 유일한 오퍼랜드의 부호입니다.이 규칙은 피연산자 또는 결과가 0이거나 무한인 경우에도 적용됩니다.
반대 부호가 있는 두 피연산자의 합(또는 유사한 부호가 있는 두 피연산자의 차이)이 정확히 0일 때, 그 합(또는 차이)의 부호는 roundTowardNegative를 제외한 모든 반올림 방향 속성에서 +0이어야 한다. 이 속성에서 정확한 제로 합(또는 차이)의 부호는 -0이어야 한다.그러나 x + x = x -(-x)는 x가 0인 경우에도 x와 같은 부호를 유지합니다.
추가 사례
기본 반올림 모드(Round-to-Near, Ties-to-Even)에서는 다음과 같이 표시됩니다.x+0.0
낳다x
(의 경우는 제외)x
-0.0
: 이, 피연산자의 합계가 이 「6.3 패러그래프 3 」 、 「 0 」 、 「 」 、 「 6 . 3 」+0.0
.
★★+0.0
원본과 비트 면에서 동일하지 않습니다.-0.0
및 그, 「」-0.0
입력으로 할 수 를 「네거티브 제로」로 .컴파일러는 잠재적인 음의 제로를 변환하는 코드를 입력할 의무가 있습니다.+0.0
.
반올림 에서는, 「」로 합니다.x+0.0
if, if, if, if.x
- 않다
-0.0
, , , 「 」x
그 자체는 허용 가능한 출력값입니다. - 하고 있다
-0.0
출력값은 다음과 같아야 합니다.+0.0
하지 않습니다).-0.0
.
곱셈의 예
디폴트 반올림 모드에서는, 이러한 문제는 발생하지 않습니다.x*1.0
.x
:
- 입니다.
x*1.0 == x
★★★★★★ 。 +/- infinity
그 결과는 다음과 같습니다.+/- infinity
같은 부호의.NaN
、 르 to to , 。IEEE 754 © 6.2.3 NaN 전파
NaN 피연산자를 그 결과로 전파하고 단일 NaN을 입력으로 갖는 동작은 수신처 형식으로 표현 가능한 경우 입력 NaN의 payload를 사용하여 NaN을 생성해야 합니다.
(의수치사)가 (수치사)라는 입니다.
NaN*1.0
입력에서 변경하지 않는 것이 좋습니다.NaN
위의 '에 되지 않았지만, '6.3p1'과 하게 지정할 수NaN
.+/- 0.0
결과, 「」가0
비트 XOR를 .1.0
【6.3p2】「 」의 비트가 에, 「 」1.0
0
이치노thus따는x*1.0 == x
★★★★★★★★★★★★★★★x
(으) 0으로 하다.
뺄셈의 경우
기본 반올림 모드에서 감산은x-0.0
도 no-op에 에 no-op도 no-op이다.왜냐하면 이것은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.0
수술 금지x == x-0.0
동음이의어
가치변화 최적화
IEEE 754-2008 표준에는 다음과 같은 흥미로운 견적이 있습니다.
IEEE 754 † 10.4 문자 그대로의 의미와 가치변화 최적화
[...]
다음과 같은 값 변경 변환은 소스 코드의 문자 그대로의 의미를 유지합니다.
- x가 0이 아니고 시그널링 NaN이 아니며 결과가 x와 같은 지수일 때 ID 속성0 + x를 적용합니다.
- x가 시그널링 NaN이 아니고 결과가 x와 같은 지수일 때 1 × x의 아이덴티티 속성을 적용한다.
- 무음 NaN의 페이로드 또는 부호 비트 변경.
- [...]
모든 NaN과 모든 무한은 동일한 지수를 공유하기 때문에 정확하게 반올림된 결과는x+0.0
그리고.x*1.0
유한하게x
와 정확히 같은 크기를 가지다x
, 그들의 지수는 같습니다.
네트워크
시그널링 NaN은 부동소수점 트랩 값입니다.이들은 부동소수점 오퍼랜드로 사용하면 Invalid Operation Exception(SIGFPE; 무효 동작 예외)이 발생하는 특수한 NaN 값입니다.예외를 트리거하는 루프가 최적화되어 있으면 소프트웨어는 더 이상 동일하게 동작하지 않습니다.
단, 사용자 2357112가 코멘트에서 지적한 바와 같이 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
선택될 수 있다: 즉, 우리가
- 권장 사항에 따라 payload를 변경하지 않고 전달합니다.
x
NaN일 때. - NaN 결과의 부호 비트를 변경하지 않음
* 1.0
. - 다음과 같은 경우 지수/제품 중에 부호 비트를 XOR하라는 명령을 따르십시오.
x
NaN이 아닙니다.
IEEE-754 안전하지 않은 최적화를 활성화하려면(x+0.0) -> x
, 플래그-ffast-math
Clang 또는 GCC로 전달해야 합니다.
x += 0.0
이면 안 된다x
결과는 사용되지 않기 때문에 최적기는 루프 전체를 제거할 수 있습니다.일반적으로 옵티마이저가 왜 결정을 내리는지는 알 수 없습니다.
언급URL : https://stackoverflow.com/questions/33272994/why-does-clang-optimize-away-x-1-0-but-not-x-0-0
'sourcecode' 카테고리의 다른 글
특정 프로세스에 대해 런타임에 로드되는 공유 라이브러리를 확인하는 방법 (0) | 2022.07.28 |
---|---|
봄에 여러 @RequestMapping 주석을 사용하는 방법 (0) | 2022.07.28 |
Java에서 'ArrayList'를 'String[]'로 변환 (0) | 2022.07.28 |
vue-router 링크가 가끔 잘못된 페이지로 연결되거나 전혀 작동하지 않는 이유는 무엇입니까? (0) | 2022.07.28 |
Vuex, 작업 상태 변경 (0) | 2022.07.28 |