C 矢量化模运算
我正在尝试编写一些相当快速的组件矢量加法代码。我正在使用(我相信是有符号的)64位整数 功能是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:
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]);
}
}