2016-11-04 2 views
3

Intel SSE/AVX/FMA 내장 함수를 사용하여 일부 수학 함수에 대해 SSE/AVX 명령어를 완벽하게 인라이닝 할 수 있습니다.컴파일러가 내장 어셈블리에 대해 생성 한 문제

다음 코드가 -march = - 64 -03 -mfma

std_fma(float, float, float):       # @std_fma(float, float, float) 
     vfmadd213ss  xmm0, xmm1, xmm2 
     ret 

_fma(float, float, float):        # @_fma(float, float, float) 
     vxorps xmm3, xmm3, xmm3 
     vmovss xmm0, xmm3, xmm0  # xmm0 = xmm0[0],xmm3[1,2,3] 
     vmovss xmm1, xmm3, xmm1  # xmm1 = xmm1[0],xmm3[1,2,3] 
     vmovss xmm2, xmm3, xmm2  # xmm2 = xmm2[0],xmm3[1,2,3] 
     vfmadd213ss  xmm0, xmm1, xmm2 
     ret 

_sqrt(float):        # @_sqrt(float) 
     vsqrtss xmm0, xmm0, xmm0 
     ret 

#include <cmath> 
#include <immintrin.h> 

auto std_fma(float x, float y, float z) 
{ 
    return std::fma(x, y, z); 
} 

float _fma(float x, float y, float z) 
{ 
    _mm_store_ss(&x, 
     _mm_fmadd_ss(_mm_load_ss(&x), _mm_load_ss(&y), _mm_load_ss(&z)) 
    ); 

    return x; 
} 

float _sqrt(float x) 
{ 
    _mm_store_ss(&x, 
     _mm_sqrt_ss(_mm_load_ss(&x)) 
    ); 

    return x; 
} 

연타 3.9 발생 조립체_sqrt 대해 생성 된 코드는 잘 동안을 감안할 (절대적으로 사용되지 않는 xmm3 레지스터를 0으로 설정하는) vxorps 및 과 비교하여 movss 명령어가 _fma 인 경우 불필요합니다 (r 컴파일러 극한 STD에 엘리 여기 -march = - 64 -03 -mfma

std_fma(float, float, float): 
     vfmadd132ss  xmm0, xmm2, xmm1 
     ret 
_fma(float, float, float): 
     vinsertps  xmm1, xmm1, xmm1, 0xe 
     vinsertps  xmm2, xmm2, xmm2, 0xe 
     vinsertps  xmm0, xmm0, xmm0, 0xe 
     vfmadd132ss  xmm0, xmm2, xmm1 
     ret 
_sqrt(float): 
     vinsertps  xmm0, xmm0, xmm0, 0xe 
     vsqrtss xmm0, xmm0, xmm0 
     ret 

과 함께 :: FMA)

GCC의 6.2 발생 조립체 불필요한 vinsertps 지시 많다

근무 예 : XMM의 정합에 https://godbolt.org/g/q1BQym

기본 64 호출 규칙 패스 부동 소수점 함수 인수 sters이므로 vmovssvinsertps 지침을 삭제해야합니다. 왜 언급 된 컴파일러가 여전히 그들을 방출합니까? 인라인 어셈블리없이 제거 할 수 있습니까?

또한 _mm_store_ss 대신 여러 개의 호출 규칙을 사용하려고 시도했지만 아무런 변화가 없었습니다.

+3

intrinsic'_mm_load_ss '의 결과는 첫 번째 요소에 32 비트 부동 소수점 값이있는 128 비트 벡터이고 다른 세 요소에는 0입니다. 이것이 불필요한 지침이하는 일이며 다른 세 요소를 0으로 설정합니다. 컴파일러는 이러한 요소가 사용되지 않았 음을 감지하고 함수가 반환 할 때 결국 파기되는 것을 감지 할만큼 똑똑하지 않지만 요청한 작업을 수행하고 있습니다. 그러나 이미 FMA 케이스에 대한 완벽한 솔루션을 갖고있는 것으로 보입니다. –

+0

이것은 정말로 나쁘다. 컴파일러는'* _ss' 내장 함수를 사용하기 때문에 컴파일러가 알아야한다. – plasmacel

+0

AFAIK, 유일한 해결책은 그렇게하지 않는 것입니다. (그리고 저는 이것이 http://stackoverflow.com/questions/39318496/how-to-merge-a-scalar-into-a-vector-without- 컴파일러 - 낭비 - 명령). 일부 경우 Clang은 상위 요소가 사용되지 않았으며이를 만지지 않을 수 있음을 확인했습니다 (링크 된 질문 참조). 옵션을 사용하여 스칼라 코드에 적용 할 수있는 경우 ('-mfma' 또는'-ffast-math'가 아닌) FMA를 사용하도록 컴파일러를 얻을 수 있지만, 지금은 무엇을 잊어 버렸고 지금 당장이를 살펴볼 시간이 없습니다. 'std :: fma'가 완벽하게 인라인되기 때문에 그냥 사용하십시오. –

답변

2

본인은 의견, 토론 및 저의 경험을 바탕으로 답변을 작성합니다. 로스 리지는 의견에서 지적

는, 컴파일러는 그 vxorpsvinsertps 지침에 다른 세 가지 요소에서 제로를 할 수 있도록 XMM 레지스터의 가장 낮은 부동 소수점 요소를 사용하는 것을 인식하는만큼 똑똑하지 않다 . 이것은 절대적으로 불필요하지만 무엇을 할 수 있습니까? 그것은 단지 내 예 _mm_fmadd_ss에서 실패 이후

필요는 연타 3.9 인텔 내장 함수에 대한 어셈블리를 생성에 GCC 6.2 (또는 현재 스냅 샷 7.0)보다 훨씬 더 일을 참고하십시오. 나는 더 많은 내장 함수를 테스트했으며 대부분의 경우 번들은 단일 명령을 내 보내는데 완벽했다.

을 수행 할 수있는 당신은 적절한 CPU 명령어를 사용할 수있는 경우가 컴파일러 내장 함수로 정의되어 있는지 희망, 표준 <cmath> 기능을 사용할 수 있습니다.

이것은 GCC처럼 NaN이와 무한대의 특수 처리하여 이러한 기능을 구현,

컴파일러는 충분하지 않습니다.따라서 intrinsics 외에도 일부 비교, 분기 및 가능한 플래그 처리를 수행 할 수 있습니다.

컴파일러 플래그 -fno-math-errno-fno-trapping-math는 추가 부동 소수점 특별한 경우와 errno 처리를 제거하기 위해 GCC그 소리 도움 않기 때문에 가능하면 그들은 하나의 지침을 방출 할 수있다 : https://godbolt.org/g/LZJyaB을.

위 플래그가 포함되어 있기 때문에 -ffast-math과 동일하게 나타낼 수 있지만 includes much more than that이며 안전하지 않은 수학 최적화와 같은 것은 바람직하지 않을 수 있습니다.

불행히도이 방법은 휴대용 솔루션이 아닙니다. 대부분의 경우 작동하지만 (godbolt 링크 참조) 여전히 구현에 의존합니다.

무엇보다

아직 훨씬 더 까다로운 휴대용하지 않고 고려해야 할 많은 일들이 있습니다 인라인 어셈블리를 사용할 수 있습니다. 그럼에도 불구하고, 그러한 간단한 한 줄 지침을 위해서는 괜찮을 수 있습니다.

고려할 사항 :

1GCC/그 소리 및 인라인 어셈블리에 대한 비주얼 스튜디오 사용하는 다른 구문, 및 Visual Studio가 64 모드에서 허용하지 않습니다. 2

당신은 AVX의 대상에 대해 VEX 인코딩 지침 (예를 들어 vsqrtss xmm0 xmm1 xmm2 3 개 연산 변종을) 방출해야하고, 비 VEX (2 개 연산 변형, 예를 들어, sqrtss xmm0 xmm1) 사전 AVX CPU에 대한 변형을 인코딩. VEX로 인 코드 된 명령어는 3 개의 오퍼랜드 명령어이므로 컴파일러가 최적화 할 수있는 자유가 더 많습니다. 이점을 취하려면 register input/output parameters을 올바르게 설정해야합니다. 그래서 아래처럼 뭔가 일을합니다.

asm("vsqrtss %1, %1, %0" :"+x"(x)); 

그것은 불필요한 이동 명령에 굴복 할 수 https://godbolt.org/g/VtNMLL을 확인하십시오

# if __AVX__ 
    asm("vsqrtss %1, %1, %0" :"=x"(x) : "x"(x)); 
# else 
    asm("sqrtss %1, %0" :"=x"(x) : "x"(x)); 
# endif 

는 그러나 다음은 VEX에 대한 나쁜 기술이다.

제 3의 Peter Cordes가 지적한 것처럼 인라인 어셈블리 기능의 경우 common subexpression elimination (CSE)constant folding (constant propagation)을 잃을 수 있습니다. 그러나 인라인 asm이 volatile으로 선언되지 않은 경우 컴파일러는이를 입력에만 의존하고 공통 하위 표현식 제거를 수행하는 순수한 함수로 처리 할 수 ​​있습니다. 피터로

는 말했다 :

"Don't use inline asm"는 절대 규칙이 아닌 당신이 을 인식하고 사용하기 전에 고려해야 뭔가입니다. 대안이 요구 사항을 충족하지 않고 으로 끝나지 않아 최적화 할 수없는 곳으로 인라인 될 경우 바로 으로 이동하십시오.

관련 문제