有没有一种使用SSE找到两个变量模数的好方法?(没有SVML)

Kel*_*Nze 5 c++ 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);
}
Run Code Online (Sandbox Code Playgroud)

问题是,这与在释放时单独取四个32位整数的每个元素的模数一样快,在调试中大约慢10倍(通过分析找到).

任何人都可以给我任何关于如何更快地使这个功能的指针?

- 我不能使用SVML因为我正在使用Visual Studio-

Z b*_*son 7

对于一般值,input并且divisors没有有用的整数除法或模数的SIMD x86指令,因此最好使用标量整数除法.但是,有一些特殊情况可以更快地完成SIMD整数模数.

例如,如果你想做(a + b)%c并且a和b已经减少(即a<cb<c),那么你可以使用这样的比较和减法:

z = a + b
if(z>=c) z-=c;
Run Code Online (Sandbox Code Playgroud)

我在这个示例中执行了这个向量化 - 模运算

另一个例子是如果除数不是编译时常数,但在循环中仍然是常数,那么你可以使用浮点除法的类似想法.浮点除法的一个常见技巧是预先计算除数的倒数并进行乘法运算,如下所示:

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

您可以使用类似的整数除法技术将整数除法转换为带移位的整数乘法.

y / x ? y * (2 n / x) >> n
Run Code Online (Sandbox Code Playgroud)

有几种不同的技术来确定因子(即魔术数)(2 n / x)和移位n.事实上,大多数编译器已经为编译时常量和除法做了这个.如果您尝试例如x/7并查看GCC或MSVC的程序集输出,您将看到它们实际上不进行整数除法,它们使用相同的幻数和移位进行乘法和移位,如http:// www. hackersdelight.org/magic.htm.

我在运行时使用Agner Fog的Vector类库或他的子例程库来完成此操作.两个库将在运行时为SSE和AVX整数除法计算幻数和移位.

但正如我在这个答案的开头所说,如果你想这样做

for(int i=0; i<n; i++) {
    z[i] = y[i]%x[i];
}
Run Code Online (Sandbox Code Playgroud)

并且x[i]在循环中不是常数,最好坚持标量除法/模数.