我在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代码路径?我不精通编写汇编,所以如果这涉及到编写任何汇编,代码示例将不胜感激。
发布于 2013-03-10 04:46:44
我通过对math.h的仔细研究找到了答案。这由一个名为_set_SSE2_enable的方法控制。这是一个有文档记录的公共符号here
启用或禁用在CRT数学例程中使用数据流单指令多数据扩展指令集2 (SSE2)指令。(此功能在x64架构上不可用,因为默认情况下会启用SSE2。)
这会导致将上述___use_sse2_mathfcns标志设置为所提供的值,从而有效地启用或禁用_pentium4 SSE2例程的使用。
文档提到这只会影响某些超越的函数,但是看一下反汇编,这似乎会影响到它们中的每个人。
编辑:单步执行每个函数都会显示它们在SSE2中都可用,但以下情况除外:
Sqrt是最大的违规者,但在SSE2中使用内部函数实现它是微不足道的。对于其他人,除了使用第三方库之外,没有简单的解决方案,但我可能没有第三方库。
发布于 2013-03-10 02:52:34
为什么不使用你自己的库而不是C运行时呢?这将为计算机之间的一致性提供更强有力的保证(假设C运行时是作为DLL提供的,并且可能会在时间上稍有变化)。
我推荐CRlibm。如果您已经以SSE2为目标,并且只要您不打算更改FPU的舍入模式,那么您就处于使用它的理想条件中,并且您将找不到更准确的实现。
发布于 2013-03-10 02:48:00
简短的答案是,你不能在你的代码中确定这个库将做什么,除非你还涉及到库实现的特定细节。这将使代码完全不可移植--即使是同一编译器的两个不同版本也可能改变库的内部结构。
当然,如果可移植性不是问题,那么使用extern <type> ___use_sse2_mathfcns;并检查它是否是真的显然是可行的。
我希望如果处理器有SSE2,并且您使用的是一个足够现代的库,那么它会尽可能地使用SSE2。但要说这一点是另一回事。
如果这对您的代码至关重要,那么实现您自己的超越函数并使用它们--这是保证相同结果的唯一方法。或者,使用一些合适的内联汇编(或超越)代码来计算选定的sin、cos等值,并将这些值与库提供的sin()和cos()函数进行比较。
https://stackoverflow.com/questions/15314390
复制相似问题