2013-12-11 5 views
1

8 개의 부동 벡터 또는 4 개의 double을 수용하기 위해 AVX를 사용하는 SSE2 사인 및 코사인 함수 (CEPHES sinf 함수를 기반으로 한 Julien Pommier의 sse_mathfun.h에서)를 변환합니다.SSE 코드를 AVX로 변환 - 비용은 _mm256_and_ps입니다.

그래서 줄리앙의 함수 sin_ps는 sin_ps8 (8 개의 부동 소수점 형)이되고 sin_pd4는 4 배가됩니다. (여기에서 "고급"편집기는 내 코드를 수락하지 않으므로 http://arstechnica.com/civis/viewtopic.php?f=20&t=1227375을 방문하십시오.)

2011 Core2 i7 @ 2.7Ghz에서 실행되는 Mac OS X 10.6.8에서 clang 3.3으로 테스트하여 벤치마킹 결과보기 이 같은

  • SINF .. -> 벡터 평가 27.7 백만/초 07 5.56e + 위에 iters (표준 스칼라 SINF() 함수)

    sin_ps .. 중 -> 41.0 백만 벡터 평가/초당 8.22e + 07 iters

    01 23,516,

    sin_pd4 ... -> 벡터 평가/초 8.06e + 07 iters

    sin_ps8 위에 40.2 백만 ... -> 벡터 평가 2.5 백만/06 초 + 5.1e 위에 iters

sin_ps8의 비용은 정말 놀랍습니다. _mm256_castsi256_ps 사용으로 인한 것 같습니다. 사실, "poly_mask = _mm256_castsi256_ps (emmm2);"줄을 주석으로 처리합니다. 보다 정상적인 성능을 제공합니다. sin_pd4는 _mm_castsi128_pd를 사용하지만 sin_ps8에서 SSE와 AVX 명령어를 섞어 쓰는 것이 아닙니다. _mm_castsi128_ps에 2 번의 호출로 _mm256_castsi256_ps 호출을 에뮬레이션하면 성능이 향상되지 않습니다. emm2와 emm0는 emmm2와 emmm0에 대한 포인터이며, v8si 인스턴스와 그러므로 (선험적으로) 32 비트 경계에 정확하게 정렬됩니다.

컴파일 가능한 코드는 sse_mathfun.hsse_mathfun_test.c을 참조하십시오.

나는 벌칙을 피하기 위해 (쉬운) 방법이 있습니까?

+0

누락 된 코드에 대해 정말 죄송합니다. 미리보기가 잘 보였지만 내 텍스트는 게시가 거부되었습니다 ... – RJVB

+0

"Core2 i7"은 무엇입니까? – Mysticial

+0

Agner Fog의 [최적화 테이블] (http://www.agner.org/optimize/#manuals)에서는 인수의 크기와 관계없이 'ANDPS' 및'VANDPS '의 Ivy Bridge 및 Haswell 대기 시간이 1 사이클임을 보여줍니다. 덧붙여서,'_mm256_castsi256_ps'는 실제로 지시를 내 보내지 않습니다. 이것은 진정한 타입 캐스트입니다. 순전히 컴파일러에 있습니다. 당신의 문제는 아마도 다른 곳에있을 것입니다. –

답변

1

레지스터에서 물건을 메모리로 옮기는 것은 대개 좋은 생각이 아닙니다. 포인터를 저장할 때마다이 작업을 수행합니다. 대신이의

이 같은

{ ALIGN32_BEG v4sf *yy ALIGN32_END = (v4sf*) &y; 
     emm2[0] = _mm_and_si128(_mm_add_epi32(_mm_cvttps_epi32(yy[0]), _v4si_pi32_1), _v4si_pi32_inv1), 
     emm2[1] = _mm_and_si128(_mm_add_epi32(_mm_cvttps_epi32(yy[1]), _v4si_pi32_1), _v4si_pi32_inv1); 
     yy[0] = _mm_cvtepi32_ps(emm2[0]), 
     yy[1] = _mm_cvtepi32_ps(emm2[1]); 
     } 

/* get the swap sign flag */ 
emm0[0] = _mm_slli_epi32(_mm_and_si128(emm2[0], _v4si_pi32_4), 29), 
emm0[1] = _mm_slli_epi32(_mm_and_si128(emm2[1], _v4si_pi32_4), 29); 

/* get the polynom selection mask 
there is one polynom for 0 <= x <= Pi/4 
and another one for Pi/4<x<=Pi/2 

Both branches will be computed. 
*/ 
emm2[0] = _mm_cmpeq_epi32(_mm_and_si128(emm2[0], _v4si_pi32_2), _mm_setzero_si128()), 
emm2[1] = _mm_cmpeq_epi32(_mm_and_si128(emm2[1], _v4si_pi32_2), _mm_setzero_si128()); 

((v4sf*)&poly_mask)[0] = _mm_castsi128_ps(emm2[0]); 
((v4sf*)&poly_mask)[1] = _mm_castsi128_ps(emm2[1]); 
swap_sign_bit = _mm256_castsi256_ps(emmm0); 

시도 뭔가 :

의견에서 언급 한 바와 같이
__m128i emm2a = _mm_and_si128(_mm_add_epi32(_mm256_castps256_ps128(y), _v4si_pi32_1), _v4si_pi32_inv1); 
__m128i emm2b = _mm_and_si128(_mm_add_epi32(_mm256_extractf128_ps(y, 1), _v4si_pi32_1), _v4si_pi32_inv1); 

y = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_cvtepi32_ps(emm2a)), _mm_cvtepi32_ps(emm2b), 1); 

/* get the swap sign flag */ 
__m128i emm0a = _mm_slli_epi32(_mm_and_si128(emm2a, _v4si_pi32_4), 29), 
__m128i emm0b = _mm_slli_epi32(_mm_and_si128(emm2b, _v4si_pi32_4), 29); 

swap_sign_bit = _mm256_castsi256_ps(_mm256_insertf128_si256(_mm256_castsi128_si256(emm0a), emm0b, 1)); 

/* get the polynom selection mask 
there is one polynom for 0 <= x <= Pi/4 
and another one for Pi/4<x<=Pi/2 

Both branches will be computed. 
*/ 
emm2a = _mm_cmpeq_epi32(_mm_and_si128(emm2a, _v4si_pi32_2), _mm_setzero_si128()), 
emm2b = _mm_cmpeq_epi32(_mm_and_si128(emm2b, _v4si_pi32_2), _mm_setzero_si128()); 

poly_mask = _mm256_castsi256_ps(_mm256_insertf128_si256(_mm256_castsi128_si256(emm2a), emm2b, 1)); 

, cast 내장 함수가 시간을 컴파일하고 지시 사항을 방출하지 순전히있다.

+0

Re : 캐스트 내장 함수 : 실제로. 나는 순수 _mm * _cast * _ps 내장 함수를 비교했으며 모두 빠르다. 그래서 SSE2 정수 코드와 두 개의 AVX 캐스트가 있고 두 캐스트 (또는 실제로 poly_mask를 계산하는 IIRC)를 모두 주석 처리하면 성능이 갑자기 상승합니다. 나는 sse_mathfun_test가 benched 함수 결과를 피연산자에 저장하여 루프 언 롤링 (성능이 너무 많이 올라갈 수 있음)을 피하는 방법을 조금 조심스럽게 해석해야합니다. 그러나 여전히 이것은 전환으로 인해 인기를 얻었습니다. – RJVB

+0

정렬 문제,로드/저장 지연, 어셈블러 코딩에 대한 지식이 너무 적습니다. 그러나 Apple Shark 도구에서 이에 대한 충분한 피드백을 보았습니다. (그리고 상어는 여기에 도움이되지 않습니다, 내 버전은 아직 AVX를 알지 못합니다.) 사실 내 CPU에는 슬프게도 AVX2가 없습니다. – RJVB

+0

내가 여기서 한 것처럼 메모리 사용을 제거하는 것이 도움이됩니까? –

1

아마도 Julien Pommier SSE 수학 함수의 이미 작동하는 AVX 확장 코드와 코드를 비교할 수 있을까요?

http://software-lisc.fbk.eu/avx_mathfun/

이 코드는 GCC에서 작동하지만 MSVC와는 수레 (float8)를 지원하지만 난 당신이 쉽게 복식 (double4)과를 사용하도록 확장 할 수 있다고 생각. sin 기능을 빨리 비교하면 SSE2 정수 부분을 제외하고는 매우 유사하다는 것을 알 수 있습니다.

+0

MSVC에서 컴파일하기 위해 sse_mathfun.h를 얻으려면 몇 가지 작업을해야한다는 것을 기억합니다. 아마도 AVX 버전에서 동일한 작업을 수행 할 수 있습니다. 나는 정직하게도 누군가가 일을했는지를보기 위해 아직 돌아 다니지 않았다. 부분적으로는 실제로 이것을 운동으로 또한하고 있기 때문이다. 그렇다면, 변수를 __m128 [2]로 액세스 할 수있게 해주는 __m256 인스턴스에 대한 포인터 대신 임시 조합과 "스칼라 포인터"를 사용하여 변환하는 것이 실제로 이점이 있습니까?캐스팅이 실제로 컴파일 만 수행되면 정렬 조정 없이는 아무런 차이가 없어야합니다. – RJVB

+0

또한, __m256과 __m128 [2]가 포함 된 공용체를 사용해 보았습니다 ... 그러나 gcc와 clang 모두 두 가지를 망설이게합니다. 어떤 경우에도 빠른 비교를 할 때 AVX2에 실제로 다른 정수 부분이 있다는 뚜렷한 인상을 받았습니다 ... 가장 쉬운 방법은 avx_sse_mathfun.h입니다. – RJVB