Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/72.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 矢量化模运算_C_Assembly_X86 64_Sse_Intrinsics - Fatal编程技术网

C 矢量化模运算

C 矢量化模运算,c,assembly,x86-64,sse,intrinsics,C,Assembly,X86 64,Sse,Intrinsics,我正在尝试编写一些相当快速的组件矢量加法代码。我正在使用(我相信是有符号的)64位整数 功能是 void addRq (int64_t* a, const int64_t* b, const int32_t dim, const int64_t q) { for(int i = 0; i < dim; i++) { a[i] = (a[i]+b[i])%q; // LINE1 } } 主回路的总成为: ..B3.4:

我正在尝试编写一些相当快速的组件矢量加法代码。我正在使用(我相信是有符号的)64位整数

功能是

void addRq (int64_t* a, const int64_t* b, const int32_t dim, const int64_t q) {
    for(int i = 0; i < dim; i++) {
        a[i] = (a[i]+b[i])%q; // LINE1
    }
}
主回路的总成为:

..B3.4:                         # Preds ..B3.2 ..B3.12
    movdqa    (%r12,%r15,8), %xmm0                          #59.22
    movdqa    %xmm8, %xmm1                                  #60.14
    paddq     (%r14,%r15,8), %xmm0                          #59.22
    call      __svml_i64rem2                                #61.9
    movdqa    %xmm0, (%r12,%r15,8)                          #61.36
    addq      $2, %r15                                      #56.30
    cmpq      %r13, %r15                                    #56.24
    jl        ..B3.4        # Prob 82%                      #56.24

因此,代码正在按预期进行矢量化。我知道,由于SVML,我可能无法获得2倍的加速,但代码运行时间为12.5秒,比完全没有矢量化时慢!这真的是这里能做的最好的吗

SSE2和AVX2都没有整数除法指令。英特尔并不真诚地将SVML函数称为内部函数,因为其中许多函数都是复杂的函数,它们映射到多条指令,而不仅仅是几条指令

有一种方法可以使用SSE2或AVX2进行更快的除法(和模运算)。看这篇文章。基本上你先计算一个除数,然后再做乘法。预计算除数需要时间,但对于代码中的某个
dim
值,它应该会胜出。我在这里更详细地描述了这个方法 我还成功地在素数查找器中实现了这个方法

Agner Fog使用该论文中描述的方法在his中实现32位(而不是64位)除法。如果您需要一些代码,这将是一个很好的起点,但您必须将其扩展到64位

编辑:根据Mysticial的评论,并假设输入已经减少,我为SSE制作了一个版本。如果这是在MSVC中编译的,那么它需要处于64位模式,因为32位模式不支持
\u mm\u set1\u epi64x
。这可以修复为32位模式,但我不想这样做

#ifdef _MSC_VER 
#include <intrin.h>
#endif
#include <nmmintrin.h>                 // SSE4.2
#include <stdint.h>
#include <stdio.h>

void addRq_SSE(int64_t* a, const int64_t* b, const int32_t dim, const int64_t q) {
    __m128i q2 = _mm_set1_epi64x(q);
    __m128i t2 = _mm_sub_epi64(q2,_mm_set1_epi64x(1));
    for(int i = 0; i < dim; i+=2) {
        __m128i a2 = _mm_loadu_si128((__m128i*)&a[i]);
        __m128i b2 = _mm_loadu_si128((__m128i*)&b[i]);
        __m128i c2 = _mm_add_epi64(a2,b2);
        __m128i cmp = _mm_cmpgt_epi64(c2, t2);
        c2 = _mm_sub_epi64(c2, _mm_and_si128(q2,cmp));
        _mm_storeu_si128((__m128i*)&a[i], c2);
    }
}

int main() {
    const int64_t dim = 20;
    int64_t a[dim];
    int64_t b[dim];
    int64_t q = 10;

    for(int i=0; i<dim; i++) {
        a[i] = i%q; b[i] = i%q;
    }
    addRq_SSE(a, b, dim, q);
    for(int i=0; i<dim; i++) {
        printf("%d\n", a[i]);
    }   
}
\ifdef\u MSC\u VER
#包括
#恩迪夫
#包括//SSE4.2
#包括
#包括
void addRq_SSE(int64_t*a、const int64_t*b、const int32_t dim、const int64_t q){
__m128i q2=_mm_set1_epi64x(q);
__m128i t2=_mm_sub_epi64(q2,_mm_set1_epi64x(1));
对于(int i=0;i对于(int i=0;对于模的函数调用正在破坏性能-您对
q
的可能值有任何先验知识吗?如果您知道输入已完全减少,则最好使用比较和条件减法。@PaulR q应该保留(基本上)运行时为常量,但编译时不知道。这有什么好处?@神秘有趣的是,条件减法只需1.9秒,这可能是合理的,但ICC没有矢量化。我不知道它有多快。@Eric你可以用SIMD执行条件运算。比较指令返回的向量为所有0或1,您可以使用另一个值从目标中减去。
#ifdef _MSC_VER 
#include <intrin.h>
#endif
#include <nmmintrin.h>                 // SSE4.2
#include <stdint.h>
#include <stdio.h>

void addRq_SSE(int64_t* a, const int64_t* b, const int32_t dim, const int64_t q) {
    __m128i q2 = _mm_set1_epi64x(q);
    __m128i t2 = _mm_sub_epi64(q2,_mm_set1_epi64x(1));
    for(int i = 0; i < dim; i+=2) {
        __m128i a2 = _mm_loadu_si128((__m128i*)&a[i]);
        __m128i b2 = _mm_loadu_si128((__m128i*)&b[i]);
        __m128i c2 = _mm_add_epi64(a2,b2);
        __m128i cmp = _mm_cmpgt_epi64(c2, t2);
        c2 = _mm_sub_epi64(c2, _mm_and_si128(q2,cmp));
        _mm_storeu_si128((__m128i*)&a[i], c2);
    }
}

int main() {
    const int64_t dim = 20;
    int64_t a[dim];
    int64_t b[dim];
    int64_t q = 10;

    for(int i=0; i<dim; i++) {
        a[i] = i%q; b[i] = i%q;
    }
    addRq_SSE(a, b, dim, q);
    for(int i=0; i<dim; i++) {
        printf("%d\n", a[i]);
    }   
}