Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angular/28.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
是否可以将myNum+;矢量化a[b[i]]*c[i];在x86_64上?_X86_X86 64_Sse_Simd_Vectorization - Fatal编程技术网

是否可以将myNum+;矢量化a[b[i]]*c[i];在x86_64上?

是否可以将myNum+;矢量化a[b[i]]*c[i];在x86_64上?,x86,x86-64,sse,simd,vectorization,X86,X86 64,Sse,Simd,Vectorization,在x86_64上,我将使用什么内部函数对以下内容进行矢量化(如果有可能进行矢量化) double myNum = 0; for(int i=0;i<n;i++){ myNum += a[b[i]] * c[i]; //b[i] = int, a[b[i]] = double, c[i] = double } double myNum=0; 对于(int i=0;i短答案否。长答案是肯定的,但效率不高。您将因执行非对齐加载而受到惩罚,这将否定任何类型的好处。除非您能够保证b[i]连

在x86_64上,我将使用什么内部函数对以下内容进行矢量化(如果有可能进行矢量化)

double myNum = 0;
for(int i=0;i<n;i++){
    myNum += a[b[i]] * c[i]; //b[i] = int, a[b[i]] = double, c[i] = double
}
double myNum=0;

对于(int i=0;i短答案否。长答案是肯定的,但效率不高。您将因执行非对齐加载而受到惩罚,这将否定任何类型的好处。除非您能够保证b[i]连续索引对齐,否则向量化后的性能很可能会更差

如果您事先知道什么是索引,最好是展开并指定显式索引。我使用模板专门化和代码生成做了类似的事情。如果您感兴趣,我可以与您分享

要回答您的评论,您基本上必须专注于一个数组。立即尝试的最简单方法是将您的循环阻塞两倍,分别加载低a和高a,然后像通常一样使用mm*\U pd。伪代码:

__m128d a, result;
for(i = 0; i < n; i +=2) {
  ((double*)(&a))[0] = A[B[i]];
  ((double*)(&a))[1] = A[B[i+1]];
  // you may also load B using packed integer instruction
  result = _mm_add_pd(result, _mm_mul_pd(a, (__m128d)(C[i])));
}
\uuum128d a,结果;
对于(i=0;i
我记不清函数名,可能需要再次检查。 另外,若您知道可能并没有别名问题,那个么在指针中使用restrict关键字。
这将使编译器更具攻击性。

这不会像现在这样进行矢量化,因为数组索引具有双重间接定向。由于使用双重索引,因此从SSE中几乎得不到什么好处,特别是因为大多数现代CPU都有2个FPU。

我将从展开循环开始。有些东西艾克

double myNum1=0,myNum2=0;

对于(int i=0;i这是我的做法,经过充分优化和测试:

#包括
__m128d和=_mm_setzero_pd();

对于(int i=0;i英特尔处理器可以执行两个浮点运算,但每个周期只能执行一次加载,因此内存访问是最严格的限制。考虑到这一点,我的目标是首先使用压缩加载来减少加载指令的数量,并使用压缩算法,因为它很方便。此后,我意识到,饱和内存带宽并不会h可能是最大的问题,如果关键是让代码运行得更快,而不是学习矢量化,那么所有与SSE指令有关的混乱可能都是过早的优化

上海证券交易所 在不假设
b
中的索引的情况下,尽可能少的加载需要将循环展开四次。一个128位加载从
b
获取四个索引,两个128位加载分别从
c
获取一对相邻的双精度加载,并收集
a
所需的独立64位加载。 这是串行代码每四次迭代7个周期的下限。(如果对
a
的访问不能很好地缓存,就足以饱和我的内存带宽)。我省略了一些恼人的事情,比如处理不是4的倍数的迭代次数

entry: ; (rdi,rsi,rdx,rcx) are (n,a,b,c)
  xorpd xmm0, xmm0
  xor r8, r8
loop:
  movdqa xmm1, [rdx+4*r8]
  movapd xmm2, [rcx+8*r8]
  movapd xmm3, [rcx+8*r8+8]
  movd   r9,   xmm1
  movq   r10,  xmm1
  movsd  xmm4, [rsi+8*r9]
  shr    r10,  32
  movhpd xmm4, [rsi+8*r10]
  punpckhqdq xmm1, xmm1
  movd   r9,   xmm1
  movq   r10,  xmm1
  movsd  xmm5, [rsi+8*r9]
  shr    r10,  32
  movhpd xmm5, [rsi+8*r10]
  add    r8,   4
  cmp    r8,   rdi
  mulpd  xmm2, xmm4
  mulpd  xmm3, xmm5
  addpd  xmm0, xmm2
  addpd  xmm0, xmm3
  jl loop
获取索引是最复杂的部分。
movdqa
从16字节对齐的地址加载128位整数数据(Nehalem对混合使用“整数”和“浮点”SSE指令有延迟惩罚).
punpckhqdq
将高64位移动到低64位,但与更简单的命名方式
movhlpd
不同,在整数模式下。32位移位在通用寄存器中完成。
movhpd
在不干扰下半部分的情况下将一个双精度加载到xmm寄存器的上半部分-这用于加载
a
这段代码明显比上面的代码快,而上面的代码又比简单的代码快,并且在每个访问模式上都是如此,但在简单的情况下,naive循环实际上是最快的。我还尝试了一些类似于SUM(a(B(:)、C(:)的函数
在Fortran中,它基本上等同于简单循环

我在一个Q6600(2.4Ghz的65纳米内核2)上测试了4个模块中的4GB DDR2-667内存。 测试内存带宽大约为5333MB/s,所以看起来我只看到了一个通道。我使用Debian的GCC4.3.2-1.1,-O3-Ffast math-msse2-Ftree vectorize-std=gnu99进行编译

对于测试,我让
n
为一百万,初始化数组,使
a[b[I]]
c[I]
都等于
1.0/(I+1)
,有一些不同的索引模式。一个分配
a
100万个元素并将
b
设置为随机排列,另一个分配
a
10万个元素并每10万个使用一次,最后一个分配
a
10万个元素并设置
b[i+1]
通过将1到9之间的随机数添加到
b[i]
。我用
gettimeofday
计时一个调用所需的时间,通过在数组上调用
clflush
清除缓存,并测量每个函数的1000次试验。我使用一些来自(特别是
统计数据
包中的核密度估计器)

带宽 现在,关于带宽的实际重要注意事项。5333MB/s,2.4Ghz时钟,每个周期刚好超过两个字节。我的数据足够长,没有任何东西可以缓存,并将循环的运行时间乘以(16+2*16+4*64)每次迭代加载的字节数(如果所有内容都未命中)几乎正好为我提供了系统拥有的~5333MB/s带宽。如果没有SSE,应该很容易使带宽饱和。即使假设
a
已完全缓存,只需读取
b
c
一次迭代即可移动12个字节的数据,天真的人也可以开始新的迭代使用流水线进行第三个周期的迭代

假设
a
上没有完全缓存,那么算术和指令的瓶颈就更少了。如果我的代码中大部分的加速都来自于对
b的加载更少,我也不会感到惊讶