5

我正在检查一些使用/fp:preciseand/fp:fast标志的代码。

根据MSDN/fp:precise文档:

在 x86 处理器上使用 /fp:precise 时,编译器将对 float 类型的变量执行舍入,以达到赋值和强制转换以及将参数传递给函数时的正确精度。这种舍入保证数据不会保留任何大于其类型容量的重要性。使用 /fp:precise 编译的程序可能比没有 /fp:precise 编译的程序更慢和更大。/fp:precise 禁用内在函数;而是使用标准运行时库例程。有关详细信息,请参阅 /Oi(生成内部函数)。

查看对sqrtf(调用/arch:SSE2,目标x86/Win32平台)的调用的反汇编:

0033185D  cvtss2sd    xmm0,xmm1  
00331861  call        __libm_sse2_sqrt_precise (0333370h)  
00331866  cvtsd2ss    xmm0,xmm0  

根据这个问题,我相信现代 x86/x64 处理器不使用 80 位寄存器(或至少不鼓励使用它们),因此编译器会执行我认为次优的操作,并使用 64 位双精度数进行计算。并且由于内部函数被禁用,因此调用了库 sqrtf 函数。

好的,很公平,这似乎符合文档所说的内容。

但是,当我为 x64 架构编译时,发生了一些奇怪的事情:

000000013F2B199E  movups      xmm0,xmm1  
000000013F2B19A1  sqrtps      xmm1,xmm1  
000000013F2B19A4  movups      xmmword ptr [rcx+rax],xmm1  

计算不是使用 64 位双精度执行的,而是使用内部函数。/fp:fast据我所知,结果与使用标志完全相同。

为什么两者之间存在差异?/fp:precise根本不适用于 x64 平台吗?

现在,作为健全性检查,我在 VS2010 x86 中使用/fp:preciseand测试了相同的代码/arch:SSE2。令人惊讶的是,sqrtpd内在函数被使用了!

00AF14C7  cvtps2pd    xmm0,xmm0  
00AF14CA  sqrtsd      xmm0,xmm0  
00AF14CE  cvtpd2ps    xmm0,xmm0 

这里发生了什么?为什么 VS2010 使用内部函数,而 VS2012 调用系统库?

针对 x64 平台测试 VS2010 的结果与 VS2012 相似(/fp:precise似乎被忽略了)。

我无法访问任何旧版本的 VS,因此无法在这些平台上进行任何测试。

作为参考,我正在使用 Intel i5-m430 处理器在 Windows 7 64 位中进行测试。

4

1 回答 1

3

首先,您应该阅读这篇关于中间浮点精度的非常好的博客文章。本文仅处理 Visual Studio 生成的代码(但这就是您的问题所在)。现在来看例子:

0033185D  cvtss2sd    xmm0,xmm1  
00331861  call        __libm_sse2_sqrt_precise (0333370h)  
00331866  cvtsd2ss    xmm0,xmm0  

此汇编代码是/fp:precise /arch:SSE2为 x86 平台生成的。根据文档,精确的浮点模型在 x86 平台上将所有计算内部提高了一倍。它还可以防止使用内在函数(我认为您已经阅读了此信息)。因此,代码首先从 float 转换为 double,然后是双精度 sqrt 调用,最后将结果转换回 float。

000000013F2B199E  movups      xmm0,xmm1  
000000013F2B19A1  sqrtps      xmm1,xmm1  
000000013F2B19A4  movups      xmmword ptr [rcx+rax],xmm1

第二个例子是为 x64 (amd64) 平台编译的,这个平台的行为完全不同!根据文档:

出于性能原因,中间运算是以任一操作数的最宽精度而不是可用的最宽精度计算的。

因此,计算将在内部以单精度完成。我认为他们还决定尽可能使用内在函数,因此在x64 平台上/fp:precise和之间的差异/fp:fast要小一些。新的行为导致更快的代码它使程序员可以更好地控制到底发生了什么(他们能够改变游戏规则,因为兼容性问题对于新的 x64 平台来说不存在问题)。不幸的是,这些更改/差异未在文档中明确说明。

00AF14C7  cvtps2pd    xmm0,xmm0  
00AF14CA  sqrtsd      xmm0,xmm0  
00AF14CE  cvtpd2ps    xmm0,xmm0 

最后,最后一个示例是使用 Visual Studio 2010 编译器编译的,我认为他们不应该使用 sqrt 的内在函数(至少对于/fp:precise模式),但他们决定在 Visual Studio 2012 中更改/修复此行为再次(见这里)。

于 2013-05-22T12:25:42.903 回答