将fpu异常或inf投入工作是否可行/有效?

时间:2014-12-06 11:54:21

标签: c++ optimization x86 sse fpu

我有这样的代码

循环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)

1 个答案:

答案 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 没有什么区别。