gpt4 book ai didi

c++ - 使用 XMM0 寄存器和内存提取(C++ 代码)比仅使用 XMM 寄存器的 ASM 快两倍 - 为什么?

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:19:18 25 4
gpt4 key购买 nike

我正在尝试实现一些内联汇编程序(在 Visual Studio 2012 C++ 代码中)以利用 SSE。我想将 7 个数字相加 1e9 次,所以我将它们从 RAM 放置到 xmm0 到 CPU 的 xmm6 寄存器。当我使用以下代码在 visual studio 2012 中使用内联汇编时:

C++代码:

for(int i=0;i<count;i++)
resVal+=val1+val2+val3+val4+val5+val6+val7;

我的 ASM 代码:

int count=1000000000;

double resVal=0.0;
//placing values to register
__asm{
movsd xmm0,val1;placing var1 in xmm0 register
movsd xmm1,val2
movsd xmm2,val3
movsd xmm3,val4
movsd xmm4,val5
movsd xmm5,val6
movsd xmm6,val7
pxor xmm7,xmm7;//turns xmm7 to zero
}

for(int i=0;i<count;i++)
{
__asm
{
addsd xmm7,xmm0;//+=var1
addsd xmm7,xmm1;//+=var2
addsd xmm7,xmm2;
addsd xmm7,xmm3;
addsd xmm7,xmm4;
addsd xmm7,xmm5;
addsd xmm7,xmm6;//+=var7
}

}

__asm
{
movsd resVal,xmm7;//placing xmm7 into resVal
}

这是 C++ 编译器为代码 'resVal+=val1+val2+val3+val4+val5+val6+val7' 反汇编的代码:

movsd       xmm0,mmword ptr [val1]  
addsd xmm0,mmword ptr [val2]
addsd xmm0,mmword ptr [val3]
addsd xmm0,mmword ptr [val4]
addsd xmm0,mmword ptr [val5]
addsd xmm0,mmword ptr [val6]
addsd xmm0,mmword ptr [val7]
addsd xmm0,mmword ptr [resVal]
movsd mmword ptr [resVal],xmm0

可以看出,编译器只使用一个 xmm0 寄存器,其他时候它从 RAM 中获取值。

两种代码(我的 ASM 代码和 C++ 代码)的答案是相同的,但是 C++ 代码的执行时间大约是我的 ASM 代码的一半!

我听说 CPU 寄存器使用它们比内存快得多。我不认为这个比率是真实的。为什么 asm 版本的 C++ 代码性能较低?

最佳答案

  • 一旦数据在缓存中(在第一个循环之后就是这种情况,如果它还不存在的话),使用内存还是寄存器都没有什么区别。
  • 首先,浮点加法比单周期加法要花费更长的时间。
  • resVal 的最终存储“解开”xmm0 寄存器以允许寄存器自由“重命名”,从而允许并行运行更多循环。

这是典型的“除非你绝对确定,否则就把代码留给编译器”。

上面的最后一个项目符号解释了为什么代码比循环的每一步都取决于先前计算的结果的代码更快。

在编译器生成的代码中,循环相当于:

movsd       xmm0,mmword ptr [val1]  
addsd xmm0,mmword ptr [val2]
addsd xmm0,mmword ptr [val3]
addsd xmm0,mmword ptr [val4]
addsd xmm0,mmword ptr [val5]
addsd xmm0,mmword ptr [val6]
addsd xmm0,mmword ptr [val7]
addsd xmm0,mmword ptr [resVal]
movsd mmword ptr [resVal],xmm0

movsd xmm1,mmword ptr [val1]
addsd xmm1,mmword ptr [val2]
addsd xmm1,mmword ptr [val3]
addsd xmm1,mmword ptr [val4]
addsd xmm1,mmword ptr [val5]
addsd xmm1,mmword ptr [val6]
addsd xmm1,mmword ptr [val7]
addsd xmm1,mmword ptr [resVal]
movsd mmword ptr [resVal],xmm1

现在,如您所见,我们可以“混合”这两个“线程”:

movsd       xmm0,mmword ptr [val1]  
movsd xmm1,mmword ptr [val1]
addsd xmm0,mmword ptr [val2]
addsd xmm1,mmword ptr [val2]
addsd xmm0,mmword ptr [val3]
addsd xmm1,mmword ptr [val3]
addsd xmm0,mmword ptr [val4]
addsd xmm1,mmword ptr [val4]
addsd xmm0,mmword ptr [val5]
addsd xmm1,mmword ptr [val5]
addsd xmm0,mmword ptr [val6]
addsd xmm1,mmword ptr [val6]
addsd xmm0,mmword ptr [val7]
addsd xmm1,mmword ptr [val7]
addsd xmm0,mmword ptr [resVal]
movsd mmword ptr [resVal],xmm0
// Here we have to wait for resval to be uppdated!
addsd xmm1,mmword ptr [resVal]
movsd mmword ptr [resVal],xmm1

我并不是说执行顺序有那么多乱序,但我当然可以看到循环的执行速度比您的循环快得多。如果你有一个备用寄存器,你可能可以在你的汇编代码中实现同样的事情[在 x86_64 中你确实有另外 8 个寄存器,尽管你不能在 x86_64 中使用内联汇编...]

(请注意,寄存器重命名不同于我的“线程”循环,它使用两个不同的寄存器 - 但效果大致相同,循环可以在它命中“resVal”更新后继续,而不必等待结果待更新)

关于c++ - 使用 XMM0 寄存器和内存提取(C++ 代码)比仅使用 XMM 寄存器的 ASM 快两倍 - 为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15349308/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com