Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/vba/17.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
Assembly AVX VMOVDQA比两个SSE MOVDQA慢?_Assembly_Sse_Bignum_Arbitrary Precision_Avx - Fatal编程技术网

Assembly AVX VMOVDQA比两个SSE MOVDQA慢?

Assembly AVX VMOVDQA比两个SSE MOVDQA慢?,assembly,sse,bignum,arbitrary-precision,avx,Assembly,Sse,Bignum,Arbitrary Precision,Avx,当我在编写fastaddloop()时,我正在使用SSE和AVX指令测试内存访问。要添加,我必须读取两个输入并生成一个输出。因此,我编写了一个伪例程,它将两个x64值读入寄存器,并将一个值写回内存,而不进行任何操作。这当然没用,我只是为了基准测试而做的 我使用一个展开的循环,每个循环处理64个字节。它由8个块组成,如下所示: mov rax, QWORD PTR [rdx+r11*8-64] mov r10, QWORD PTR [r8+r11*8-64] mov QWORD PTR [rcx+

当我在编写fastaddloop()时,我正在使用SSE和AVX指令测试内存访问。要添加,我必须读取两个输入并生成一个输出。因此,我编写了一个伪例程,它将两个x64值读入寄存器,并将一个值写回内存,而不进行任何操作。这当然没用,我只是为了基准测试而做的

我使用一个展开的循环,每个循环处理64个字节。它由8个块组成,如下所示:

mov rax, QWORD PTR [rdx+r11*8-64]
mov r10, QWORD PTR [r8+r11*8-64]
mov QWORD PTR [rcx+r11*8-64], rax
movdqa xmm0, XMMWORD PTR [rdx+r11*8-64]
movdqa xmm1, XMMWORD PTR [r8+r11*8-64]
movdqa XMMWORD PTR [rcx+r11*8-64], xmm0
vmovdqa ymm0, YMMWORD PTR [rdx+r11*8-64]
vmovdqa ymm1, YMMWORD PTR [r8+r11*8-64]
vmovdqa YMMWORD PTR [rcx+r11*8-64], ymm0
然后我把它升级到SSE2。现在我使用4个块,如下所示:

mov rax, QWORD PTR [rdx+r11*8-64]
mov r10, QWORD PTR [r8+r11*8-64]
mov QWORD PTR [rcx+r11*8-64], rax
movdqa xmm0, XMMWORD PTR [rdx+r11*8-64]
movdqa xmm1, XMMWORD PTR [r8+r11*8-64]
movdqa XMMWORD PTR [rcx+r11*8-64], xmm0
vmovdqa ymm0, YMMWORD PTR [rdx+r11*8-64]
vmovdqa ymm1, YMMWORD PTR [r8+r11*8-64]
vmovdqa YMMWORD PTR [rcx+r11*8-64], ymm0
后来我使用了AVX(每个寄存器256位)。我有两个这样的街区:

mov rax, QWORD PTR [rdx+r11*8-64]
mov r10, QWORD PTR [r8+r11*8-64]
mov QWORD PTR [rcx+r11*8-64], rax
movdqa xmm0, XMMWORD PTR [rdx+r11*8-64]
movdqa xmm1, XMMWORD PTR [r8+r11*8-64]
movdqa XMMWORD PTR [rcx+r11*8-64], xmm0
vmovdqa ymm0, YMMWORD PTR [rdx+r11*8-64]
vmovdqa ymm1, YMMWORD PTR [r8+r11*8-64]
vmovdqa YMMWORD PTR [rcx+r11*8-64], ymm0
到目前为止,还不是非常壮观。有趣的是基准测试结果:当我在1k+1k=1k 64位字上运行三种不同的方法时(即两次8 kb的输入和一次8 kb的输出),我得到了奇怪的结果。以下每个计时用于将两次64字节的输入处理为64字节的输出

  • x64寄存器方法以大约15个周期/64字节的速度运行
  • SSE2方法以大约8.5个周期/64字节的速度运行
  • AVX方法以大约9个周期/64字节的速度运行
我的问题是:为什么AVX方法比SSE2方法慢(虽然不是很多)?我希望它至少是平价。使用YMM寄存器会花费这么多额外的时间吗?内存已对齐(否则将获得GPF)


有人对此有解释吗?

在Sandybridge/Ivybridge上,256b AVX加载和存储被破解为加载/存储执行单元中的两个128b操作[正如Peter Cordes所指出的,这些操作并不完全是µ操作,但需要两个周期才能清除端口],因此,没有理由期望使用这些指令的版本更快

为什么会慢一些?我想到了两种可能性:

  • 对于基+索引+偏移量寻址,128b负载的延迟为6个周期,而256b负载的延迟为7个周期(英特尔优化手册中的表2-8)。虽然您的基准测试应该受thoughput而不是延迟的约束,但延迟越长,处理器从任何故障(管道冒泡、预测失误或中断服务等)中恢复所需的时间就越长,这确实会产生一些影响

  • 在同一文档的11.6.2中,Intel建议,256b负载的缓存线和跨页代价可能比128b负载的更大。如果您的加载并非全部32字节对齐,这也可以解释您在使用256b加载/存储操作时看到的减速:

示例11-12显示了未对齐的SAXPY的两种实现 地址。备选方案1使用32字节加载,备选方案2使用16字节加载 字节加载。这些代码示例使用两个源缓冲区执行, src1,src2,在32字节对齐的4字节偏移处,以及 目标缓冲区,DST,是32字节对齐的。使用两个16字节的 代替32字节内存访问的内存操作执行得更快


我似乎还记得,在当前的体系结构上,在某些情况下,AVX内存访问被分成两个单独的128位访问。也许这就是你在这里遇到的。当你开始进行实际计算时,AVX的真正好处就来了,因为你显然可以并行地做两倍于SSE的计算。啊,很有趣。你对此有什么建议吗?快速搜索发现了这一点,但他们声称内存路径已满256位:还要小心将遗留(非VEX)SSE指令与AVX指令混合使用-在没有看到其余基准测试代码的情况下,不清楚这是否相关,但无论如何,您可能应该知道:我不太喜欢SSE和AVX版本中的8乘法器,您需要使用16和32(但在寻址中不能这样做),当然,运行循环的次数更少。你的基准代码正确地解释了这一点吗?@Paul R:谢谢你的评论,但是当从一个指令集切换到另一个指令集时,惩罚是要付出的。但我的整个AVX循环只使用AVX和常规指令,不使用SSE/FP。注意,Haswell的情况并非如此,它是在我最初编写此答案后发布的。它不是2个UOP,但在执行单元中需要2个周期来完成这两部分。AGU仅在第一个周期中需要,在第二个周期中是免费的(例如,计算存储地址),这就是为什么SnB/IvB设计人员认为不需要包含单独的存储地址端口的原因。哈斯韦尔有一个,因为它可以在一个周期内完成2560亿次转移。无论如何,1个uop与否的区别在于管道的4个uop/循环吞吐量。未对齐的负载/存储不会是问题,因为OP使用了
vmovdqa
,这会在未对齐时出错。不过,包括这一段仍然会让答案更好。