【问题标题】:Strange /fp Floating Point Model flag behavior奇怪的 /fp 浮点模型标志行为
【发布时间】:2013-04-03 04:55:47
【问题描述】:

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

根据MSDN documentation/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  

来自this question 我相信现代 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:precise/arch:SSE2 测试了相同的代码。令人惊讶的是,sqrtpd 内部函数正在被使用!

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

这里发生了什么?为什么VS2010使用intrinsic而VS2012调用系统库?

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

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

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

【问题讨论】:

  • 这真的很奇怪。我知道/fp:precise 有时会导致编译器酌情将中间体提升到更高的精度。但这并不能解释这里的完全不一致。
  • “从这个问题我相信 x86 架构没有 80 位寄存器”再来一次?
  • 是的,奇怪的措辞。更新以阐明反对使用它们的一般建议。
  • @PascalCuoq 回复对您(已删除)答案的评论。有 3 种模式,precisefaststrict。 AFAIK,precise 表示编译器将尝试生成尽可能精确的结果。 (可能以性能为代价)fast 是不言自明的,与 GCC 的 ffast-math 相同。 strict 严格遵循 IEEE。

标签: c visual-studio-2010 visual-studio-2012 floating-point sse


【解决方案1】:

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

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

这个汇编代码是用/fp:precise /arch:SSE2 为x86 平台生成的。根据documentation 的说法,精确浮点模型在x86 平台上 将所有计算内部提升为两倍。它还可以防止使用内在函数(我想你已经读过this information)。因此,代码从 float 到 double 的转换开始,然后是双精度 sqrt 调用,最后将结果转换回 float。

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

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

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

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

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

最后,最后一个示例是使用 Visual Studio 2010 编译器编译的,我认为他们不小心使用了 sqrt 的内在函数(至少对于 /fp:precise 模式),但他们决定更改/修复在 Visual Studio 2012 中再次出现这种行为(请参阅 here)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多