23

我在 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 代码路径?我不擅长编写程序集,因此如果这涉及编写任何程序集,将不胜感激代码示例。

4

3 回答 3

11

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

启用或禁用在 CRT 数学例程中使用 Streaming SIMD Extensions 2 (SSE2) 指令。(此功能在 x64 架构上不可用,因为默认启用 SSE2。)

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

文档中提到这仅影响某些先验功能,但从反汇编来看,这似乎影响了它们中的每个人。

编辑:进入每个功能表明它们在 SSE2 中都可用,除了以下内容:

  • fmod
  • 科什
  • 平方

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

于 2013-03-09T20:46:44.103 回答
4

为什么不使用您自己的库而不是 C 运行时?这将为跨计算机的一致性提供更强有力的保证(假设 C 运行时是作为 DLL 提供的,并且可能会随着时间发生轻微变化)。

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

于 2013-03-09T18:52:33.783 回答
2

简短的回答是,除非您还涉及库实现的特定细节,否则您无法在您的代码中确定库将做什么。这些会使代码完全不可移植——即使是同一编译器的两个不同版本也可能会改变库的内部结构。

当然,如果可移植性不是问题,那么使用extern <type> ___use_sse2_mathfcns;并检查它是否是真实的显然会起作用。

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

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

于 2013-03-09T18:48:00.090 回答