x86汇编指令优化

我试图优化一个循环中的一段指令,称为数千次,这是我算法的瓶颈。

该代码块计算N个矩阵3x3(iA阵列)与N个向量3(iV阵列)的相乘并将N个结果存储在oV阵列中。 (N不固定,通常在3000到15000之间)

每行矩阵和向量都是128位对齐的(4个浮点数),以利用SSE优化(忽略第四个浮点值)。

C ++代码:

  __m128* ip = (__m128*)iV;
  __m128* op = (__m128*)oV;
  __m128* A = (__m128*)iA;

  __m128 res1, res2, res3;
  int i;

  for (i=0; i<N; i++)
  {
    res1 = _mm_dp_ps(*A++, *ip, 0x71);
    res2 = _mm_dp_ps(*A++, *ip, 0x72);
    res3 = _mm_dp_ps(*A++, *ip++, 0x74);

    *op++ = _mm_or_ps(res1, _mm_or_ps(res2, res3));
  }

编译器生成这些指令:

000007FEE7DD4FE0  movaps      xmm2,xmmword ptr [rsi]               //move "ip" in register
000007FEE7DD4FE3  movaps      xmm1,xmmword ptr [rdi+10h]           //move second line of A in register
000007FEE7DD4FE7  movaps      xmm0,xmmword ptr [rdi+20h]           //move third line of A in register
000007FEE7DD4FEB  inc         r11d                                 //i++
000007FEE7DD4FEE  add         rbp,10h                              //op++
000007FEE7DD4FF2  add         rsi,10h                              //ip++
000007FEE7DD4FF6  dpps        xmm0,xmm2,74h                        //dot product of 3rd line of A against ip
000007FEE7DD4FFC  dpps        xmm1,xmm2,72h                        //dot product of 2nd line of A against ip
000007FEE7DD5002  orps        xmm0,xmm1                            //"merge" of the result of the two dot products
000007FEE7DD5005  movaps      xmm3,xmmword ptr [rdi]               //move first line of A in register
000007FEE7DD5008  add         rdi,30h                              //A+=3
000007FEE7DD500C  dpps        xmm3,xmm2,71h                        //dot product of 1st line of A against ip
000007FEE7DD5012  orps        xmm0,xmm3                            //"merge" of the result
000007FEE7DD5015  movaps      xmmword ptr [rbp-10h],xmm0           //move result in memory (op)
000007FEE7DD5019  cmp         r11d,dword ptr [rbx+28h]             //compare i
000007FEE7DD501D  jl          MyFunction+370h (7FEE7DD4FE0h)       //loop

我对低级优化不太熟悉,所以问题是:如果我自己编写汇编代码,您是否会看到一些可能的优化?

例如,如果我改变它会运行得更快:

add         rbp,10h
movaps      xmmword ptr [rbp-10h],xmm0

通过

movaps      xmmword ptr [rbp],xmm0
add         rbp,10h

我也读过ADD指令比INC快


用偏移量来计算间接地址,比如rbp-10是非常便宜的,因为在“有效地址计算”单元中有这种计算的特殊硬件[我认为它有不同的名称,但不能想到或有任何与谷歌成功找到它的名字]。

然而, add rbp,10h[rbp-10h]之间存在依赖关系,这可能会导致问题 - 但在这种特殊情况下我怀疑它。 在你的情况下, rbp-10和使用它之间有很长的距离,所以这不是问题。 编译器可能会把它放得太远,因为它在那时是“免费的”,因为处理器将等待数据从外部进入先前读取的xmm寄存器。 换句话说,我们可以在循环开始时读取xmm0xmm1xmm2之间的任何工作,以及使用xmm0xmm1xmm2dpps指令将会有所帮助,因为处理器将等待那些数据“到达“,然后才能计算dpps结果。


我已经完成了很多x86汇编优化,我可以告诉你这是一个很好的学习经验。 它教会了我很多关于编译器如何工作的知识,而且我学到的最重要的事情是编译器一般都很擅长。 我知道这是一个轻浮的评论,但这是真的......

我还了解到,您所做的优化可以在一个处理器系列上产生积极的结果,并在另一个处理器系列产生负面结果。 诸如流水线,分支预测和处理器缓存之类的东西发挥了巨大的作用...所以除非您针对的是非常具体的硬件配置,否则请注意您所做的改进的假设。

对于重新排序添加以删除rbp-10h偏移量的具体问题......它看起来像是一个明显的改进,而且您必须通过阅读说明手册进行验证,但是我猜测-10h内存偏移是为了在该指令中免费。 移动add可能会抛出流水线指令,实际上会导致时钟周期丢失。 你必须试验。


有几件事你可以通过上面的代码来改善它。 通常,在修改后使用一个值会导致处理器停顿,因为它会等待结果。 所以这些线将会受到惩罚: -

add         rbp,10h
movaps      xmmword ptr [rbp-10h],xmm0

但是在这两行之上的代码片段相距很远,所以这不是一个真正的问题。 正如其他人已经说过的那样, rbp-10h是地址计算硬件处理它的'免费'。

你可以将movaps xmm3,xmmword ptr [rdi]移动一行并重新排列其他几行。

这会值得吗?

没有

由于算法的原因,你很幸运能够看到任何真正的性能增益

<blink> memory bandwidth limited </blink>*

这意味着从RAM读取数据到CPU的时间大于CPU执行处理的时间。 在最坏的情况下,读取内存地址可能涉及页面错误和磁盘读取。 prefetch指令也无济于事,它被称为'流式SIMD扩展',因为它经过优化以将数据流式传输到CPU(存储器接口可以处理四个独立的流IIRC)。

如果你在一小组数据上进行大量计算(可能是FFT),那么你可以从手工编制汇编器中获得很多。 但是,你的算法非常简单,所以CPU在等待数据到达的时候空闲很多时间。 如果你知道RAM的速度,你可以计算算法的最大吞吐量,并用它来比较它目前达到的数据(尽管如此,你永远不会达到最大理论吞吐量)。

你可以做的事情是尽量减少内存停顿,这是一个更高层次的变化,而不是单个指令的摆弄(通常,优化算法会得到更好的结果)。 最简单的方法是双重缓冲输入数据。 将寄存器组分成两组(可能在这里做,因为你只使用四个SIMD寄存器): -

  load set 1
mainloop:
  load set 2
  do processing on set 1
  save set 1 result
  load set 1
  do processing on set 2
  save set 2 result
  goto mainloop

希望这给了你一些想法。 即使速度并不快,它仍然是一个有趣的练习,你可以从中学到很多东西。

  • RIP闪烁。
  • 链接地址: http://www.djcxy.com/p/72409.html

    上一篇: x86 assembly instructions optimisation

    下一篇: What is the difference between MOV and LEA?