我有这样的代码
循环10 M:
if( fz != 0.0)
{
fhx += hx/fz;
}
这被称为10 M次循环需要非常快 - 我只需要抓住fz不为零时的情况,不要使div为零错误,但这是一种非常罕见的情况, 确实在10M的情况下它应该是零,我不知道一次,两次或更新
我可以在某种程度上摆脱这10M的ifs并使用“nan / inf”或者可能会捕获异常并继续吗? (如果fz为零,我需要fhx + = 0.0,我的意思是没有什么只是继续 ?将fpu异常或inf投入工作是否可能/有效?
(我使用c ++ / mingw32)
答案 0 :(得分:1)
你可以,但它可能没那么有用。在这种情况下,掩蔽不会有用。
异常发生时非常缓慢,首先在CPU进入内核级别异常处理程序之前必须发生许多微编码复杂的事情,然后它必须以复杂而缓慢的方式将其交给您的进程。另一方面,当它们没有发生时,它们不会花费任何成本。
但是,只要分支是可预测的,并且基本上从不采用的分支是,比较和分支也不会真正花费任何成本。当然,要让它们完全发生需要花费一点吞吐量,但它们并不处于关键路径中......但即使它们存在,这里真正的问题是每次迭代都是分裂。
无论如何,该分区的吞吐量为每14个循环1个(在Haswell上 - 在其他μarch上更差),除非fz
特别“好”,即使那时它每8个循环1个(再次在Haswell上)。在Core2上它更像19和5,在P4上它更像是(以典型的P4方式)每71个循环一个分区,无论如何。
一个预测良好的分支和比较刚刚消失。在我的4770K上,进行比较和分支之间的差异消失在噪音中(也许如果我运行足够多次,我最终将获得统计上显着的差异,但它会很小),两者都随机获胜一半的时间。我用于此基准的代码是
global bench
proc_frame bench
push r11
[endprolog]
xor ecx, ecx
mov rax, rcx
mov ecx, -10000000
vxorps xmm1, xmm1
vxorps xmm2, xmm2
vmovapd xmm3, [rel doubleone]
_bench_loop:
imul eax, ecx, -0xAAAAAAAB ; distribute zeroes somewhat randomly
shr eax, 1 ; increase to make more zeroes
vxorps xmm0, xmm0
vcvtsi2sd xmm0, eax
vcomisd xmm0, xmm1 ; #
jz _skip ; #
vdivsd xmm0, xmm3, xmm0
vaddsd xmm2, xmm0
_skip:
add ecx, 1
jnz _bench_loop
vmovapd xmm0, xmm2
pop r11
ret
endproc_frame
另一个功能是相同的,但两行标有#注释掉。
当零的数量增加时最终始终获胜的版本是具有分支的版本,表示除以零明显慢于分支错误预测。这是没有甚至使用异常机制来创建一个程序员可见的异常,它只是从运行的微编码“奇怪的案例修复”事件的成本。但是你没有那么多的零,所以,
TL; DR 没有什么区别。