如何控制C数学是否使用SSE2?

时间:2013-03-09 18:32:37

标签: c++ visual-c++ floating-point sse deterministic

我在fp:strict模式下使用MSVC进入了C库的超越数学函数的汇编。他们似乎都遵循相同的模式,这是sin发生的事情。

首先,从名为“disp_pentium4.inc”的文件中有一个调度例程。它检查变量___use_sse2_mathfcns是否已设置;如果是,请拨打__sin_pentium4,否则拨打__sin_default

__sin_pentium4(在“sin_pentium4.asm”中)首先将参数从x87 fpu传递到xmm0寄存器,使用SSE2指令执行计算,然后将结果加载回fpu。

__sin_default(在“sin.asm”中)将变量保留在x87堆栈上,只需调用fsin

因此,在这两种情况下,操作数都被推送到x87堆栈并返回它,使其对调用者透明,但如果定义了___use_sse2_mathfcns,则操作实际上是在SSE2而不是x87中执行的

这种行为对我来说非常有趣,因为x87超越函数因具有略微不同的行为而臭名昭着,具体取决于实现,而给定的SSE2代码应始终提供可重现的结果。

有没有办法在编译或运行时确定是否会使用SSE2代码路径?我不是很精通编写程序集,所以如果这涉及编写任何程序集,那么代码示例将不胜感激。

3 个答案:

答案 0 :(得分:9)

我通过仔细调查math.h找到了答案。这由名为_set_SSE2_enable的方法控制。这是一个记录在案的公共符号here

  

启用或禁用Streaming SIMD Extensions 2(SSE2)   CRT数学例程中的指令。 (此功能不可用   x64体系结构,因为默认情况下启用SSE2。)

这会导致上述___use_sse2_mathfcns标志被设置为提供的值,从而有效地启用或禁用_pentium4 SSE2例程。

文档中提到这只影响某些超越函数,但是看看反汇编,这似乎影响了它们的每个人。

编辑:单步执行每个功能都会发现它们在SSE2中都可用,除了以下内容:

  • fmod
  • 的sinh
  • COSH
  • 的tanh
  • SQRT

Sqrt是最大的攻击者,但使用内在函数在SSE2中实现它是微不足道的。对于其他人来说,除了使用第三方库之外没有简单的解决方案,但我可能没有。

答案 1 :(得分:4)

为什么不使用自己的库而不是C运行时?这将为计算机之间的一致性提供更强大的保证(可能是C运行时作为DLL提供,并且可能会在时间上略有变化)。

我会推荐CRlibm。如果您已经针对SSE2,并且只要您不打算更改FPU的舍入模式,那么您就处于使用它的理想条件下,并且您将找不到更准确的实现。

答案 2 :(得分:2)

简短的回答是,除非您还涉及图书馆实施的具体细节,否则您无法告诉您的代码库以确定图书馆将做什么。这些将使代码完全不可移植 - 即使同一编译器的两个不同版本可能会更改库的内部。

当然,如果可移植性不是问题,那么使用extern <type> ___use_sse2_mathfcns;并检查它是否真实就行了。

我希望如果处理器具有SSE2并且您使用的是足够现代的库,那么它将尽可能使用SSE2。但要说肯定是另一回事。

如果这对您的代码至关重要,那么实现您自己的超越函数并使用它们 - 这是保证相同结果的唯一方法。或者,使用一些合适的内联汇编程序(或超越)代码来计算选定的sincos等值,并将这些值与sin()cos()函数进行比较。图书馆。