我在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代码路径?我不是很精通编写程序集,所以如果这涉及编写任何程序集,那么代码示例将不胜感激。
答案 0 :(得分:9)
我通过仔细调查math.h找到了答案。这由名为_set_SSE2_enable
的方法控制。这是一个记录在案的公共符号here:
启用或禁用Streaming SIMD Extensions 2(SSE2) CRT数学例程中的指令。 (此功能不可用 x64体系结构,因为默认情况下启用SSE2。)
这会导致上述___use_sse2_mathfcns标志被设置为提供的值,从而有效地启用或禁用_pentium4 SSE2例程。
文档中提到这只影响某些超越函数,但是看看反汇编,这似乎影响了它们的每个人。
编辑:单步执行每个功能都会发现它们在SSE2中都可用,除了以下内容:
Sqrt是最大的攻击者,但使用内在函数在SSE2中实现它是微不足道的。对于其他人来说,除了使用第三方库之外没有简单的解决方案,但我可能没有。
答案 1 :(得分:4)
为什么不使用自己的库而不是C运行时?这将为计算机之间的一致性提供更强大的保证(可能是C运行时作为DLL提供,并且可能会在时间上略有变化)。
我会推荐CRlibm。如果您已经针对SSE2,并且只要您不打算更改FPU的舍入模式,那么您就处于使用它的理想条件下,并且您将找不到更准确的实现。
答案 2 :(得分:2)
简短的回答是,除非您还涉及图书馆实施的具体细节,否则您无法告诉您的代码库以确定图书馆将做什么。这些将使代码完全不可移植 - 即使同一编译器的两个不同版本可能会更改库的内部。
当然,如果可移植性不是问题,那么使用extern <type> ___use_sse2_mathfcns;
并检查它是否真实就行了。
我希望如果处理器具有SSE2并且您使用的是足够现代的库,那么它将尽可能使用SSE2。但要说肯定是另一回事。
如果这对您的代码至关重要,那么实现您自己的超越函数并使用它们 - 这是保证相同结果的唯一方法。或者,使用一些合适的内联汇编程序(或超越)代码来计算选定的sin
,cos
等值,并将这些值与sin()
和cos()
函数进行比较。图书馆。