c++ - 这个环路优化配置文件意味着什么?

标签 c++ assembly optimization

我开始使用 VTune。作为一个教育示例,我正在尝试在 Debug模式下进行一些微优化。这是我的代码库中的玩具示例。此代码出现在 C++ 非 const 方法中,“.data_length”是对象的一个​​ int 字段(偏移 32 字节),通常是一个大数:

for (int i=0;i<data_length;++i) { /*...*/ }

VTune 很有帮助地向我展示了 for 循环的程序集(来自 MSVC 2013)。注意性能数字,以秒为单位(我删除了所有未注册的时间)。我还添加了一些注释:

0x140433084 mov dword ptr [rsp+0x588], 0x0 |       | ;"i=0"
0x14043308f jmp 0x1404330a1 <Block 77>     |       | ;jump to compare and loop body
                                           |       |
0x140433091 Block 76:                      |       | ;"++i"
0x140433091 mov eax, dword ptr [rsp+0x588] | 0.451 |
0x140433098 inc eax                        | 0.002 |
0x14043309a mov dword ptr [rsp+0x588], eax |       |
                                           |       |
0x1404330a1 Block 77:                      |       | ;if (!(i<data_length)) goto next section
0x1404330a1 mov rax, qword ptr [rsp+0x6f0] | 0.407 |
0x1404330a9 mov eax, dword ptr [rax+0x20]  |       | ;  move "data_length" into "eax".
0x1404330ac cmp dword ptr [rsp+0x588], eax | 1.195 | ;  "i<data_length;"
0x1404330b3 jnl 0x140433106 <Block 80>     |       |
0x1404330b5 Block 78:                      |       |
. . .                                      |       | ;Loop body.  There's a jmp in here to
                                           |       | ;  block 76.
                                           |       |
0x140433106 Block 80:                      |       | ;code following loop

这告诉我加载 i 以增加它会导致缓存失败(为什么不是寄存器,天哪?)。其次,测试逻辑非常缓慢——尤其是每次加载“.data_length”。


我想,为什么不加载一次然后使用递减:

for (int i=data_length-1;i>=0;--i) { /*...*/ }

组装和时序如下:

0x140433084 mov rax, qword ptr [rsp+0x6f0] |       | ;Same code, but now only happens once!
0x14043308c mov eax, dword ptr [rax+0x20]  |       |
0x14043308f dec eax                        |       | ;"data_length-1"
0x140433091 mov dword ptr [rsp+0x588], eax |       | ;"i=data_length-1;"
0x140433098 jmp 0x1404330aa <Block 77>     |       | ;jump to compare and loop body
                                           |       |
0x14043309a Block 76:                      |       | ;"++i"
0x14043309a mov eax, dword ptr [rsp+0x588] | 0.357 |
0x1404330a1 dec eax                        | 0.002 |
0x1404330a3 mov dword ptr [rsp+0x588], eax |       |
                                           |       |
0x1404330aa Block 77:                      |       | ;if (i<0) goto next section
0x1404330aa cmp dword ptr [rsp+0x588], 0x0 | 0.401 | ;  "i>=0;"
0x1404330b2 jl 0x140433105 <Block 80>      | 2.806 |
0x1404330b4 Block 78:                      |       |
. . .                                      |       | ;Loop body.  Same as above, I think.
                                           |       |
0x140433105 Block 80:                      |       | ;code following loop

看看那个jl!跳跃三秒?我想也许这个位置不在指令缓存中,但正如您所看到的,它实际上非常接近(正如您所期望的那样,就在循环体之后)。更重要的是,无论如何第一种方法应该有同样的问题。第一个版本的 jnl 甚至没有注册。

我的猜测是它的计时在循环体中被吃掉了——虽然奇怪的是它发生在一种情况下而不发生在另一种情况下。我是否还有更多工作要做?

这都是我写的,现在再看我觉得可能是一个无聊的分支预测问题。 CPU 喜欢在循环中向后执行分支,但在这种情况下,大部分时间不应该执行到 block 80 的分支。


我肯定还在学习这个,所以假设我基本上正确地注释了所有内容,我有几个问题:

  1. 我认为 i 应该是一个寄存器,并且在优化模式下它会变成一个寄存器,我的想法是否正确?
  2. 第二个版本中的 jl 发生了什么?是否确实是分支预测失败?为什么它没有出现在下一条指令中?

编辑:正在测试的 CPU 是 Intel 990X(Gulftown,2011)。

最佳答案

i 可能应该在寄存器中(例如,如果编译器已优化);然而,循环体(未显示)中的代码也有可能使用所有寄存器,并且为了提高性能,避免将 i 放入寄存器(特别是如果该循环体包含内部循环或函数调用之类的)。

我不知道您的 CPU 是什么(VIA、AMD、Intel;Atom、Xeon;有多旧),但现代 CPU 中的分支预测应该可以很好地处理该分支。但是,当循环终止时,您可能会预期会发生分支预测错误,并且如果迭代次数较少(例如 data_length-1 较小),那么单个错误预测可能会很严重。

关于c++ - 这个环路优化配置文件意味着什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30229572/

相关文章:

c++ - 为空间优化 tribools 数组

c++ - 如何有效地计算两点之间的角度?

c++ - CUBLAS内存分配错误

c++ - 了解 const 运算符

c++ - 枚举类型布局是否与其基础类型兼容?

c++ - 打开简历;如何释放 IplImage*?

assembly - "self-modified link"在 Pegasus 编程中如何工作?

assembly - sbb指令和进位标志的使用

node.js - 资源优化 : which one to use Knapsack, 蛮力或任何其他方法?

c++ - 使用内联 ASM c++ 显示 640x480 BMP 图像