能力值:
( LV2,RANK:10 )
2 楼
c++里写一下呗,release版
能力值:
( LV2,RANK:10 )
3 楼
debug下代码:
00401826 xor ecx,ecx
00401828 cmp dword ptr [ebp-4],0
0040182C setge cl
0040182F dec ecx
00401830 and ecx,64h
00401833 mov edx,dword ptr [ebp-4]
00401836 add edx,ecx
00401838 mov dword ptr [ebp-4],edx
能力值:
( LV2,RANK:10 )
4 楼
release版:
00401010 |. 8B4424 00 MOV EAX,DWORD PTR SS:[ESP]
00401014 |. 33C9 XOR ECX,ECX
00401016 |. 85C0 TEST EAX,EAX
00401018 |. 0F9DC1 SETGE CL
0040101B |. 49 DEC ECX
0040101C |. 83E1 64 AND ECX,64
0040101F |. 03C1 ADD EAX,ECX
能力值:
( LV2,RANK:10 )
5 楼
为什么要写x = x + (x < 0 ? 100 : 0);这样的代码呢?完全可以写成这样:
if(x < 0)
x += 100;
这样编译出的汇编代码应该是这样的:
test eax,eax
jge Label
add eax,64
Label:
岂不是更有效率?(我没编译,你可以自己编译一下看是不是这样。)
如果想获得最高效的程序,那就写简单的C代码,一句代码只执行一个最简单基本的操作,经过编译器优化后,往往可以得到比自己写汇编还高效的机器码,毕竟现在的编译器优化太无敌了。
把C代码写的太紧凑的话,得到的机器码往往不会被最佳优化。
能力值:
( LV9,RANK:330 )
6 楼
5楼的有个误区
if(x < 0)
x += 100;
反而是不优化的
跳转指令导致指令预取流水线效率低下
1楼和4楼已经是不用SIMD的情况下C/C++最优化的结果了
用asm的话,还可以优化一点
__asm {
mov eax, x
cdq ; edx = x >= 0 ? 0 : 0xFFFFFFFF
and edx, 0x64 ; edx = x >= 0 ? 0 : 100
add x, edx
}
这样就避免了判断和跳转
想要进一步优化,把数据对齐,然后用MMX/SSE2/SSE3/SSE4......
PS:想让编译器最大限度优化,尽量多用 ? : 少用 if
不过代码可读性会变差,自己权衡吧
能力值:
( LV2,RANK:10 )
7 楼
竟忘了cdq,膜拜ls,不过如果这段代码运行次数很多的话(lz说上亿次),跳转指令就不会影响效率了,在经过前面的几次跳转后CPU会记住正确的跳转目标,以后就不会错了。
能力值:
( LV2,RANK:10 )
8 楼
CPU会记住正确的跳转目标吗?
不会吧?
CPU只会千篇一律地按固定格式解码。
也就是说,如果第一次跳转CPU跳错了,又清除流水线重来。
那么下次的时候,CPU还是会跳错,因为CPU判断预取跳转后
的指令的算法是不变的。在同一个地方只会发生同样的事情,
就是同样的跳错。
当然,可以使用跳转预示前缀来告诉CPU,预取指令是取跳转后
的指令还是非跳转而紧接着的下一个EIP。
能力值:
( LV2,RANK:10 )
9 楼
test eax,eax
jge Label
add eax,64
Label:
=========================以上为代码段A
__asm {
mov eax, x
cdq ; edx = x >= 0 ? 0 : 0xFFFFFFFF
and edx, 0x64 ; edx = x >= 0 ? 0 : 100
add x, edx
}
=========================以上为代码段B
我并不认为代码段B比代码段A的 执行速度会更快?
并不一定的。
能力值:
( LV2,RANK:10 )
10 楼
代码段A,我改为
cmp x,o
jnb lable
add x,64
lable:
能力值:
( LV2,RANK:10 )
11 楼
Nehalem同时也降低了同步原语(起始同步),比如LOCK prefix、XCHG和CMPXCHG等指令的延迟。同步原语在多线程编程中是必需的,而多线程的扩展性被同步所限制,通过降低延迟,可以提高现在多线程软件的性能。INTEL宣称,Nehalem的LOCK CMPXCHG指令(其作用是使整个流水线串行化)的延迟是P4的约20%,Core 2的约60%。尽管降低了延迟,但行为仍然和以前的CPU还是一样的,锁定指令(Lock)并不是管道化的,即使后面的操作可以被提前到锁定指令之前来执行。
==========================
Nehalem改进了宏操作融合(macro-op fusion)。Core 2可以在32bit模式下,把TEST/CMP(比较指令)和其后的Jcc(条件分支指令)给解码融合成一个微操作(uop):CMP+Jcc。这样就增加了解码带宽,并减少了微操作数量。而Nehalem的宏操作融合则包括了更多的条件分支指令:JL/JNGE, JGE/JNL, JLE/JNG, JG/JNLE。TEST/CMP和这些条件分支指令都将被解码成一个微操作:CMP+ Jcc。而且,Nehalem的宏操作融合可以是32bit和64bit模式。这是很重要的,因为现在大多数服务器都运行的是64bit操作系统,即使是桌面电脑也开始更多的欢迎64bit操作系统
能力值:
( LV3,RANK:20 )
12 楼
... Nehalem改进了宏操作融合(macro-op fusion)。Core 2可以在32bit模式下,把TEST/CMP(比较指令)和其后的Jcc(条件分支指令)给解码融合成一个微操作(uop):CMP+Jcc。这样就增加了解码带宽,并减少了微操作数量。 ...
是micro-op fusion(“微码融合”)吧。
能力值:
( LV9,RANK:330 )
13 楼
就这段代码来说,分支是不可预测的,因为每次x的值都不一样
fusion不代表没有penalty
intel的manual也说道:
[LEFT]3.4.1.1 Eliminating Branches Eliminating branches improves performance because: [/LEFT]• [LEFT]It reduces the possibility of mispredictions. [LEFT]• [/LEFT] [/LEFT] [LEFT]It reduces the number of required branch target buffer (BTB) entries. Conditional [LEFT]branches, which are never taken, do not consume BTB resources. There are four principal ways of eliminating branches: • [/LEFT] [/LEFT] [LEFT]Arrange code to make basic blocks contiguous. [LEFT]• [/LEFT] [/LEFT] [LEFT]Unroll loops, as discussed in Section 3.4.1.7, “Loop Unrolling.” [LEFT]• [/LEFT] [/LEFT] [LEFT]Use the CMOV instruction. • Use the SETCC instruction.
fusion是在无法消除branch时最后的办法
不过比起这个,cache miss的penalty更多
x的值最好在内存中连续分布,并且size尽量不要超过L1/L2/L3
PS:
cmp x, 0
jnb lable
这个不可以fusion
[LEFT]The first instruction of the macro-fused pair must be a CMP or TEST instruction. This instruction can be REG-REG, REG-IMM, or a micro-fused REG-MEM comparison. The second instruction (adjacent in the instruction stream) should be a conditional[/LEFT] branch.
MEM-IMM是不支持的
[/LEFT]
能力值:
( LV2,RANK:10 )
14 楼
CPU会记住正确的跳转目标吗? 不会吧? CPU只会千篇一律地按固定格式解码。 也就是说,如果第一次跳转CPU跳错了,又清除流水线重来。 那么下次的时候,CPU还是会跳错,因为CPU判断预取跳转后 的指令的算法是不变的。在同一个地方只会发生同样的事情, 就是同样的跳错。
如果是这样的话那么分支预测技术全部都没用喽?
就这段代码来说,分支是不可预测的,因为每次x的值都不一样
嗯,如果单从这两行代码来看的话,分支确实是不可预测的,但是lz所说“运算次数达到几亿次”,我理解为x为一个很大的负数,或者在一个执行次数很多的循环内,就像这样:
for(;;)
{
x = x + ( x < 0 ? 100 : 0);
//其它操作,还有决定啥时候break
}
否则很难会有哪行代码执行上亿次,如果是这样的话,BTB显然可以工作的很好,而且效率会很高。
能力值:
( LV9,RANK:330 )
15 楼
对循环来说,分支预测是有效的
对x = x + ( x < 0 ? 100 : 0)来说,分支预测是无效的
而且,就算分支预测正确,还是有penalty的
消除分支才是上策
能力值:
( LV2,RANK:10 )
16 楼
真的很感谢大家的回复。很抱歉最近Kx币都用完了。。就只能给10个了。。。
能力值:
( LV2,RANK:10 )
17 楼
如果x初始值为3,每次x = x + ( x < 0 ? 100 : 0)之前都x-=5,那分支预测有效吗?
能力值:
( LV9,RANK:330 )
18 楼
能力值:
( LV2,RANK:10 )
19 楼
看来是用不到分支预测的。。。谢谢你~