我开始使用VTune。作为一个教育示例,我在调试模式下尝试了一些微优化。这是我的代码库中的玩具示例。此代码显示在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
来增加它会导致缓存失败(为什么不是寄存器,geez?)。其次,测试逻辑非常缓慢 - 特别是加载&#34; .data_length&#34;每一次。
我想,为什么不加载一次然后使用递减:
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喜欢在循环中向后移动分支,但在这种情况下,阻止80 的分支不应该在大多数情况下被占用。
我肯定还在学习这个,所以假设我对所有内容进行了大致正确的注释,我有几个问题:
i
应该是一个寄存器,并且它将成为优化模式中的一个?jl
发生了什么?确实是分支预测失败了吗?为什么它不显示在下一条指令上?编辑:正在测试的CPU是Intel 990X(Gulftown,2011)。
答案 0 :(得分:0)
android:fitsSystemWindows
应该在寄存器中(例如,如果编译器已优化);然而,循环体中的代码(未示出)也可能使用所有寄存器,并且它可以更好地保证性能,以避免将i
放入寄存器(特别是如果该循环体)包含内部循环或函数调用或其他东西)。
我不知道你的CPU是什么(VIA,AMD,Intel; Atom,Xeon;多大年纪),但现代CPU中的分支预测应该能很好地处理这个分支。但是,当循环终止时,您可能期望分支误预测,并且如果迭代次数较少(例如i
很小),则单个误预测可能很重要。