使用最新Haswell芯片的VS2013编译器在x64中进行数值回归?

les*_*ode 17 visual-c++ visual-studio-2013

我们最近开始看到我们的构建机器上的单元测试失败(某些数值计算超出了容差).经过调查,我们发现我们的一些开发人员无法重现测试失败.简而言之,我们最终将问题追溯到似乎是舍入错误的问题,但是这个错误只发生在最新Haswell芯片(我们的构建服务器最近升级到其上)的x64版本上.我们缩小了范围并从我们的一个测试中取出了一个计算:

#include "stdafx.h"
#include <cmath>

int _tmain(int argc, _TCHAR* argv[])
{
  double rate = 0.0021627412080263146;
  double T = 4.0246575342465754;
  double res = exp(-rate * T);
  printf("%0.20e\n", res);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

当我们在VS2013中编译这个x64(包括默认的编译器开关/fp:precise)时,它会在较旧的Sandy Bridge芯片和较新的Haswell芯片上给出不同的结果.不同之处在于第15位有效数字,我认为这两位机器上的机器epsilon都是双倍的.

如果我们在VS2010或VS2012(或者顺便说一下,VS2013 x86)中编译相同的代码,我们在两个芯片上得到完全相同的答案.

在过去几年中,我们经历了许多版本的Visual Studio和许多不同的英特尔芯片进行测试,没有人能够回想起我们必须根据芯片之间的不同舍入误差来调整我们的回归测试期望.

这显然导致了开发人员与旧版和新版硬件之间的游戏,以及对测试的期望应该是什么......

在VS2013中是否存在我们需要使用的编译器选项以某种方式缓解差异?

更新:

Results on Sandy Bridge developer PC:
VS2010-compiled-x64: 9.91333479983898980000e-001
VS2012-compiled-x64: 9.91333479983898980000e-001
VS2013-compiled-x64: 9.91333479983898980000e-001

Results on Haswell build server:
VS2010-compiled-x64: 9.91333479983898980000e-001
VS2012-compiled-x64: 9.91333479983898980000e-001
VS2013-compiled-x64: 9.91333479983899090000e-001
Run Code Online (Sandbox Code Playgroud)

更新:

我使用procexp来捕获加载到测试程序中的DLL列表.

Sandy Bridge developer PC:
apisetschema.dll
ConsoleApplication8.exe
kernel32.dll
KernelBase.dll
locale.nls
msvcr120.dll
ntdll.dll

Haswell build server:
ConsoleApplication8.exe
kernel32.dll
KernelBase.dll
locale.nls
msvcr120.dll
ntdll.dll
Run Code Online (Sandbox Code Playgroud)

Han*_*ant 7

您记录的结果受MXCSR寄存器的值影响,选择舍入模式的两位在此处很重要.要获得您喜欢的"快乐"数字,您需要强制处理器向下舍入.像这样:

#include "stdafx.h"
#include <cmath>
#include <float.h>

int _tmain(int argc, _TCHAR* argv[]) {
    unsigned prev;
    _controlfp_s(&prev, _RC_DOWN, _MCW_RC);
    double rate = 0.0021627412080263146;
    double T = 4.0246575342465754;
    double res = exp(-rate * T);
    printf("%0.20f\n", res);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:0.99133347998389898000

更改_RC_DOWN_RC_NEAR使MXCSR处于正常舍入模式,即操作系统在启动程序之前对其进行编程的方式.其中产生0.99133347998389909000.或者换句话说,您的Haswell机器实际上产生了预期的价值.

究竟如何发生这种情况可能很难诊断,控制寄存器是您能想到的最糟糕的全局变量.的通常的原因是注入DLL重编程的FPU.调试器可以显示加载的DLL,比较两台机器之间的列表以查找候选项.