2014-07-14 4 views
4

(SVML없이) 계수 부문의 사용을 요구하고 그래서 나는이 (죄송이이 overcommented 것)을 할 썼다 :SSE를 사용하여 두 변수의 모듈러스를 찾는 좋은 방법이 있습니까? 내가 SSE, 내가 제작 된 프로그램 중 하나를 사용하는 방법을 배우게하기 위해 노력하고있어

__m128i SSEModDiv(__m128i input, __m128i divisors) 
{ 
    //Error Checking (div by zero) 
    /*__m128i zeros = _mm_set1_epi32(0); 
    __m128i error = _mm_set1_epi32(-1); 
    __m128i zerocheck = _mm_cmpeq_epi32(zeros, divisors); 
    if (_mm_extract_epi16(zerocheck, 0) != 0) 
     return error; 
    if (_mm_extract_epi16(zerocheck, 2) != 0) 
     return error; 
    if (_mm_extract_epi16(zerocheck, 4) != 0) 
     return error; 
    if (_mm_extract_epi16(zerocheck, 6) != 0) 
     return error;*/ 

    //Now for the real work 
    __m128 inputf = _mm_cvtepi32_ps(input); 
    __m128 divisorsf = _mm_cvtepi32_ps(divisors); 

    /*__m128 recip = _mm_rcp_ps(divisorsf); //Takes reciprocal 
    __m128 divided = _mm_mul_ps(inputf, recip); //multiplies by reciprical values*/ 
    __m128 divided = _mm_div_ps(inputf, divisorsf); 
    __m128i intermediateint = _mm_cvttps_epi32(divided); //makes an integer version truncated 
    __m128 intermediate = _mm_cvtepi32_ps(intermediateint); 
    __m128 multiplied = _mm_mul_ps(intermediate, divisorsf); //multiplies the intermediate with the divisors 
    __m128 mods = _mm_sub_ps(inputf, multiplied); //subtracts to get moduli 
    return _mm_cvtps_epi32(mods); 
} 

문제는 릴리스에서 개별적으로 4 개의 32 비트 정수의 각 요소의 모듈러스를 취하는 것과 마찬가지로 대략 빠르며 (프로파일 링으로 찾은) 디버그에서 약 10 배 더 느립니다.

누구든지이 기능을 더 빠르게 만드는 방법에 대한 지침을 줄 수 있습니까? 나는 그것이 스칼라를 사용하는 것이 가장 좋습니다 그래서 inputdivisors는 정수 나누기 또는 계수에 대한 유용한 SIMD의 86 지침이없는의 일반적인 값의 비주얼 Studio-

+0

[yeppp] (http://www.yeppp.info/#arguments)를 보았습니까? 비록 모듈로가 가속화 된 연산 중 하나라고 확신하지는 않지만 –

+0

제수는 일반적인 경우의 모든 다른 값이거나 일반적으로 모두 동일한 값입니까? –

+0

나는 yeppp를 보았다. modulo 함수를 제공하지 않는다 ...실제로 인텔 내장 함수를 훨씬 뛰어 넘는 것은 제공하지 않는 것 같습니다. 순수 내장 함수보다 빠릅니다. –

답변

5

을 사용하고 원인

- 난 SVML 될 사용할 수 없습니다 정수 나누기. 그러나 SIMD 정수 모듈을보다 빠르게 수행 할 수있는 특별한 경우가 있습니다.

예를 들어

당신은 (a가 b +) % c와 a와 b가 이미 감소 수행하려는 경우 (즉, a<cb<c) 다음과 같은 비교 및 ​​뺄셈 사용할 수 있습니다

z = a + b 
if(z>=c) z-=c; 

을 이 예제에서이 작업을 수행했습니다. vectorizing-modular-arithmetic

또 다른 예는 제수가 컴파일 타임 상수가 아니지만 루프 내에서 상수 인 경우 부동 소수점 나누기에서 유사한 아이디어를 사용할 수 있다는 것입니다.

float fact = 1.0/x; 
for(int i=0; i<n; i++) { 
    z[i] = fact*y[i]; //z[i] = y[i]/x; 
} 

당신은 변화와 정수 곱셈에 정수 나누기를 변환 정수 나누기에 대한 유사한 기술을 사용할 수 있습니다 : 부동 소수점 부문의 일반적인 트릭은 제수의 역수를 미리 계산하고이 같은 곱셈 할 것입니다 .

y/x ≈ y * (2 n/x) >> n 

는 (일명 magic number) (2 n/x) 요인과 변화 n를 결정하는 여러 가지 방법이 있습니다. 사실 대부분의 컴파일러는 이미 컴파일시의 상수와 나누기를 위해이 작업을 수행 할 것입니다. 예를 들어 x/7을 시도하고 GCC 또는 MSVC에서 어셈블리 출력을 보면 정수 분할을 실제로하지 않는다는 것을 알 수 있습니다. http://www.hackersdelight.org/magic.htm에서 계산 된 것과 동일한 마법 번호와 시프트를 사용하여 곱셈과 시프트를 수행합니다.

나는 Agner Fog의 Vector Class Library 또는 그의 Subroutine library을 사용하여 런타임에이 작업을 수행합니다. 두 라이브러리는 런타임시 SSE 및 AVX 정수 나누기에 대한 사용자의 마법 수 및 이동을 계산합니다.

는하지만이 답변의 시작 부분에서 말했듯이 당신은

for(int i=0; i<n; i++) { 
    z[i] = y[i]%x[i]; 
} 

x[i] 그것이 스칼라 부문/계수에 충실하는 것이 가장 좋습니다 루프 내에서 일정하지 수행하려는 경우.