gcc의 빠른 수학은 실제로 무엇을 하는가?


144

나는 gcc를 이해한다 --ffast-math 플래그가 플로트 op의 속도를 크게 높이고 IEEE 표준을 벗어나는 것을 알고 있지만 실제로 켜져있을 때 발생하는 정보에 대해서는 찾을 수 없습니다. 누구든지 세부 사항 중 일부를 설명하고 깃발이 켜져 있거나 꺼져 있으면 어떻게 변할 것인지에 대한 명확한 예를 제시 할 수 있습니까?

비슷한 질문에 대해 SO를 파고 들었지만 ffast-math의 작동을 설명하는 것을 찾을 수 없었습니다.

답변:


86

언급했듯이 엄격한 IEEE 준수를 유지하지 않는 최적화가 가능합니다.

예를 들면 다음과 같습니다.

x = x*x*x*x*x*x*x*x;

x *= x;
x *= x;
x *= x;

부동 소수점 산술은 연관성이 없으므로 연산의 순서 및 인수 분해는 반올림으로 인한 결과에 영향을줍니다. 따라서이 최적화는 엄격한 FP 동작에서 수행되지 않습니다.

실제로 GCC가 실제로이 특정 최적화를 수행하는지 확인하지 않았습니다. 그러나 아이디어는 동일합니다.


25
@Andrey :이 예에서는 7 곱하기에서 3으로 줄입니다.
Mysticial

4
@Andrey : 수학적으로 맞습니다. 그러나 다른 반올림으로 인해 마지막 몇 비트에서 결과가 약간 다를 수 있습니다.
신비주의

1
대부분의 경우이 작은 차이는 중요하지 않습니다 (상대적으로 10 ^ -16 double정도이지만 응용 프로그램에 따라 다름). 주목할 점은 ffast-math 최적화가 반드시 "더 많은"반올림을 추가 할 필요는 없다는 것입니다. IEEE와 호환되지 않는 유일한 이유는 답변이 작성된 것과 다르기 때문입니다.
Mysticial

1
@user : 오류의 크기는 입력 데이터에 따라 다릅니다. 결과에 비해 작아야합니다. 예를 들어, x10보다 작은 경우 Mystical의 예에서 오류는 약 10 ^ -10입니다. 그러나 그렇다면 x = 10e20오류는 수백만이 될 것입니다.
벤 Voigt

3
@stefanct 실제로는의에 대해 -fassociative-math어떤에 포함되어 -funsafe-math-optimizations있는 다시 활성화됩니다 -ffast-math 하지 않습니다 GCC 최적화 왜 a*a*a*a*a*a(a*a*a)*(a*a*a)?
phuclv

255

-ffast-math 엄격한 IEEE 준수를 깨뜨리는 것 이상을 수행합니다.

우선, 물론 깨집니다 엄격한 IEEE 준수를 , 예를 들어 부동 소수점에서 수학적으로 동일하지만 (이상적으로) 동일하지 않은 것으로 명령을 재정렬 할 수 있습니다.

둘째, 단일 명령어 수학 함수 후에 설정 을 비활성화 합니다 errno. 이는 스레드 로컬 변수에 대한 쓰기를 피함을 의미합니다 (일부 아키텍처에서 해당 함수에 대해 100 % 차이를 만들 수 있음).

셋째, 모든 수학이 유한 한 것으로 가정합니다. 즉 , 해로운 영향을 미칠 수있는 NaN (또는 0) 검사가 수행되지 않습니다. 이것은 일어나지 않을 것이라고 간단히 가정합니다.

넷째, 나누기와 역 제곱근에 대한 역 근사 를 가능하게 합니다.

또한 부호있는 0 (목표가 지원하더라도 부호있는 0이 존재하지 않는다고 가정) 및 반올림 수학을 비활성화하여 컴파일 타임에 일정하게 접을 수 있습니다.

마지막으로,이 더 하드웨어 인터럽트 때문에 수학을 트래핑 / 신호 일어날 수 있다고 가정 코드 생성 (이 결과적으로 목표 아키텍처에서 사용할 수 있고 할 수없는 경우입니다 일어나지 않는을 , 그들은 처리되지 않습니다).


15
데이먼, 고마워! 참조를 추가 할 수 있습니까? 마찬가지로 gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html " -ffast-math 설정 -fno-수학 errno를, -funsafe - 수학 - 최적화, -ffinite - 수학 만, -fno-반올림 - 수학, -fno-신호 -nans 및 -fcx-limited-range.이 옵션을 사용하면 전 처리기 매크로 FAST_MATH 가 정의됩니다. "및 ( math.hmath_errhandling 근처에서) 와 같은 glibc의 항목 " 기본적으로 모든 함수는 errno 및 예외 처리를 모두 지원합니다. gcc의 빠른 연산 모드 및 인라인 함수가 정의 된 경우 이는 사실이 아닐 수 있습니다. "
osgx

4
@javapowered : "위험한지"여부는 필요한 것을 보장합니다. -ffast-math컴파일러는 모서리를 자르고 약속을 어길 수 있습니다 (설명대로). 일반적으로 위험하지 않으며 대부분의 사람들에게 문제가되지 않습니다. 대부분의 사람들은 동일하고 더 빠릅니다. 그러나 코드 이러한 약속을 가정하고 의존하는 경우 코드가 예상과 다르게 동작 할 수 있습니다. 일반적으로 이는 프로그램이 대부분 제대로 작동하는 것처럼 보이지만 일부 결과는 "예상치 못한"결과를 가져올 수 있습니다 (예 : 물리 시뮬레이션에서는 두 객체가 제대로 충돌하지 않을 수 있음).
Damon

2
@Royi : 둘은 서로 독립적이어야합니다. -O2일반적으로 속도에 맞게 거래하는 것을 제외하고는 "모든"법적 최적화가 가능합니다. -O3또한 속도를 위해 크기를 최적화하는 최적화를 가능하게합니다. 여전히 100 % 정확성을 유지합니다. -ffast-math일반적으로 해를 끼치 지 않지만 표준의 표현에 의해 잘못된 것으로 간주 될 수있는 "약간 부정확 한"행동을 허용함으로써 수학 연산을 더 빠르게하려고 시도합니다. 코드가 참이면 많은 두 컴파일러 (단지 1 ~ 2 %)에 속도가 다른 다음 코드가 엄격하게 기준을 준수하는지 확인하고 ...
데이먼

1
... 제로 경고를 생성합니다. 또한 앨리어싱 규칙 및 자동 벡터화와 같은 방식으로 방해받지 않도록하십시오. 원칙적으로 GCC는 적어도 MSVC만큼 성능이 우수해야합니다 (보통 제 경험상 더 우수합니다). 그렇지 않은 경우 MSVC가 무시하지만 GCC가 최적화를 비활성화하는 미묘한 실수를 저지른 것 같습니다. 둘 다 원한다면 두 가지 옵션을 모두 제공해야합니다.
Damon

1
@Royi : 그 코드는 나에게 정말 작고 단순하지 않으며 몇 분 (또는 몇 시간)에 깊이 분석 할 수있는 것이 아닙니다. 무엇보다도, 그것은 무해한 것처럼 보이며 #pragma omp parallel for루프 본문 내에서 함수 인수가 가리키는 주소를 읽고 쓰며 사소한 양의 분기를 수행합니다. 교육받지 못한 추측으로, 구현 정의 스레드 호출 내에서 캐시를 스 래싱 할 수 있으며 MSVC는 앨리어싱 규칙이 요구하는 중간 저장소를 잘못 피할 수 있습니다. 말할 수 없습니다.
Damon
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.