SSE/AVX와 함께 FMA(Fused Multiple-Add) 지침을 사용하는 방법
나는 일부 Intel/AMD CPU가 SSE/AVX를 사용하여 시뮬레이션과 추가를 할 수 있다는 것을 배웠다.
샌디 브리지 및 SSE2/AVX/AVX2의 사이클당 플롭스.
나는 코드에서 이것을 가장 잘 할 수 있는 방법을 알고 싶고 CPU 안에서 어떻게 하는지 알고 싶다. 즉, 초경량 아키텍처에서 말이다.SSE에서 다음과 같은 긴 금액을 하고 싶다고 하자.
//sum = a1*b1 + a2*b2 + a3*b3 +... where a is a scalar and b is a SIMD vector (e.g. from matrix multiplication)
sum = _mm_set1_ps(0.0f);
a1 = _mm_set1_ps(a[0]);
b1 = _mm_load_ps(&b[0]);
sum = _mm_add_ps(sum, _mm_mul_ps(a1, b1));
a2 = _mm_set1_ps(a[1]);
b2 = _mm_load_ps(&b[4]);
sum = _mm_add_ps(sum, _mm_mul_ps(a2, b2));
a3 = _mm_set1_ps(a[2]);
b3 = _mm_load_ps(&b[8]);
sum = _mm_add_ps(sum, _mm_mul_ps(a3, b3));
...
내 질문은 이것이 어떻게 동시 곱셈과 덧셈으로 전환되는가 하는 것이다.데이터가 종속될 수 있는가?내 말은 CPU가 할 수 있다는 것이다._mm_add_ps(sum, _mm_mul_ps(a1, b1))
동시에 또는 곱셈과 덧셈에 사용되는 레지스터는 독립적이어야 하는가?
마지막으로 Haswell과 함께 FMA에 어떻게 적용되는가?이다_mm_add_ps(sum, _mm_mul_ps(a1, b1))
단일 FMA 명령 또는 마이크로 조작으로 자동 변환되는가?
컴파일러는 분리된 덧셈과 곱셈을 융합할 수 있다. 비록 이것이 최종 결과를 변화시키더라도(더 정확하게 만들어짐)
FMA는 반올림(내부 임시 곱셈 결과에 대해 사실상 무한정 정밀도를 유지하며), ADD + MUL은 두 개의 반올림을 가진다.
IEEE 및 C 표준은 다음과 같은 경우에 이를 허용한다.#pragma STDC FP_CONTRACT ON
유효하며, 컴파일러는 디폴트로 그것을 가질 수 있다(그러나 모든 것이 다 그렇지는 않다).Gcc는 기본적으로 FMA로 계약됨(기본값)-std=gnu*
, 그러나 아니다.-std=c*
예)-std=c++14
) 클랑의 경우 로만 활성화된다.-ffp-contract=fast
. (그냥.#pragma
활성화됨, 다음과 같은 단일 표현 내에서만a+b*c
, 별개의 C++ 문으로 구분하지 않음).
이것은 엄격한 것과 이완된 부동소수점(또는 gcc 용어로,-ffast-math
대-fno-fast-math
입력 값에 따라 라운딩 오차를 증가시킬 수 있는 다른 종류의 최적화를 허용한다.이것은 FMA 내부 임시의 무한한 정밀성 때문에 특별하다. 내부 임시의 라운딩이 있었다면 엄격한 FP에서는 허용되지 않을 것이다.
완화 부동 소수점을 활성화하더라도 컴파일러는 이미 본질을 사용하고 있다면 당신이 무엇을 하고 있는지 알 것으로 예상할 수 있기 때문에 여전히 퓨즈를 선택하지 않을 수 있다.
따라서 원하는 FMA 지침을 실제로 얻을 수 있는 가장 좋은 방법은 제공된 본질을 실제로 사용하는 것이다.
FMA3 본질: (AVX2 - Intel Haswell)
_mm_fmadd_pd()
_mm256_fmadd_pd()
_mm_fmadd_ps()
_mm256_fmadd_ps()
- 그리고 엄청난 다른 변주곡들도...
FMA4 본질: (XOP - AMD 불도저)
_mm_macc_pd()
_mm256_macc_pd()
_mm_macc_ps()
_mm256_macc_ps()
- 그리고 엄청난 다른 변주곡들도...
GCC 5.3, Clang 3.7, ICC 13.0.1 및 MSVC 2015(컴파일러 버전 19.00)에서 다음과 같은 코드를 테스트했다.
float mul_add(float a, float b, float c) {
return a*b + c;
}
__m256 mul_addv(__m256 a, __m256 b, __m256 c) {
return _mm256_add_ps(_mm256_mul_ps(a, b), c);
}
올바른 컴파일러 옵션(아래 참조)으로 모든 컴파일러는vfmadd
지만시:vfmadd213ss
으로부터mul_add
그러나 MSVC만이 계약하지 않는다.mul_addv
단신으로vfmadd
지만시:vfmadd213ps
).
과 같은 vfmadd
지시사() mul_addv
MSVC와 함께).
GCC: -O2 -mavx2 -mfma
Clang: -O1 -mavx2 -mfma -ffp-contract=fast
ICC: -O1 -march=core-avx2
MSVC: /O1 /arch:AVX2 /fp:fast
GCC 4.9는 계약되지 않음mul_addv
단 하나의 fma 지침이지만 GCC 5.1 이상이다.나는 다른 컴파일러들이 언제부터 이런 일을 시작했는지 모르겠다.
'IT이야기' 카테고리의 다른 글
v-if, v-else, v-else-if에서 동일한 구성 요소 태그가 Vue 인스턴스를 공유하지 못하도록 방지 (0) | 2022.05.20 |
---|---|
C에서 함수를 호출하기 전의 파라미터 평가 순서 (0) | 2022.05.20 |
중복 키를 해시맵에 넣으면 어떻게 되는가? (0) | 2022.05.20 |
제출의 필드를 비우십시오(ve-validated vue). (0) | 2022.05.20 |
자바에서 예외를 던지지 않고 스택 추적을 버리는 방법이 있는가? (0) | 2022.05.20 |