【问题标题】:Run test vs. Debug test = different results运行测试与调试测试 = 不同的结果
【发布时间】:2013-09-08 23:32:15
【问题描述】:

我们有一些单元测试来检查线性方程组的解的结果,将浮点数与增量进行比较。

尝试调整增量时,我注意到 Visual Studio Run testDebug test 模式之间的相同数字略有变化。

为什么会这样?当我调试测试时,#if DEBUG 部分被禁用,因此执行的代码应该是相同的。

谢谢。

【问题讨论】:

  • 你能用例子展示代码吗?
  • 如果#if DEBUG 被禁用,这是否意味着您通常正在构建发布版本?如果是这样,那么是的,优化和未优化的代码在处理浮点值方面存在细微差别。
  • 我们正在使用 Console.WriteLine() 打印发布版本中的预期值,然后将它们与“运行测试”的实际值进行比较,它们是不同的,我们如何打印相同的值?
  • 您必须在发布版本上运行测试(无论如何您都应该这样做)。
  • 您的意思是当我们选择“调试测试”时,即使我们当前的 Visual Studio 配置为 Release,也会使用 Bin\Debug 文件夹中的程序集?

标签: c# .net visual-studio-2010 visual-studio unit-testing


【解决方案1】:

有关在典型 DEBUG 和 RELEASE 构建(未优化与优化)之间产生不同结果的简单代码示例,请在 LINQPad 中尝试:

void Main()
{
    float a = 10.0f / 3;
    float b = 10;
    b /= 3;

    (a == b).Dump();
    (a - b).Dump();
}

如果您在优化的情况下执行此操作(确保 LINQPad 窗口中一直向下到右侧的小按钮变为“/o+”),您将得到以下结果:

False
-7,947286E-08

如果你禁用它,关闭优化,你会得到这个:

True
0

注意生成的IL代码是一样的:

请注意,地址不同,这可能表明这里除了纯 IL 之外还有其他东西,尽管我不知道那可能是什么。

【讨论】:

  • 这很有趣。什么优化导致这种情况发生?他们只是在调试环境中节省一些位而不是额外的精度吗?
  • @NWard 在调试中,它需要将值写入本地,以便它们可供您在调试器中显示。这意味着它们不能纯粹保存在寄存器中,因此 JIT 需要使用不同的方法。寄存器比托管原语更宽,因此它将值写入失去精度/准确性的本地变量。
  • 我们一直在 Release Visual Studio 配置中工作。为什么浮点结果从Run testDebug test 发生变化?
【解决方案2】:

有各种各样的因素会影响浮点计算,其中最重要的是它是否真的将值写入本地/字段。对于优化的构建,JIT 可能能够将值保存在寄存器中 - FPU 寄存器为 80 位宽,以最大限度地减少累积错误。如果它需要将值实际写入 32 位 (float) 或 64 位 (double) 本地或字段,它必然会丢失其中的一些。所以是的,如果它可以在寄存器中完成所有工作 - 它可以给出不同(通常更“正确”)的结果,而不是将中间值写入本地等。

还有其他可用的寄存器,但我怀疑这里是否在使用这些寄存器:XMM/SSE 寄存器是 128 位的; SIMD 可以(取决于机器)高达 512 位。

【讨论】:

  • @Alberto 在这里定义“正确”和“预期”。你想看到什么价值观?
  • 有没有办法从 Visual Studio Release 版本中获得正确的预期值?我的意思是我可以在单元测试中比较的东西。这里来自 Visual Studio 输出、运行测试和调试测试的值不同...
  • 当你说“正确”和“预期”时,你必须定义什么是正确的并声明你的期望。两种模型略有不同,没有任何迹象表明一种是错误的,另一种是正确的。如果要存储这些值,您将失去该精度。正确的方法是针对优化的代码运行测试,无论如何你都应该这样做。为什么要测试您不会发送给客户的代码?这是您要发布的代码,您应该进行测试。
  • @Alberto 将浮点数与相等性进行比较几乎是“做错事”的定义。比较浮点的唯一可靠/推荐的方法是减去它们并检查差异的绝对值(即非负)是否小于某个任意小的数字。基本上:“足够接近”
  • @LasseV.Karlsen 很公平,我错过了“与三角洲”。但我会说,在这种情况下,“需要一个更大的增量”——增量需要准确地适应这就是:四舍五入。
【解决方案3】:

如果您运行构建,它将使用完整的 jit 优化执行,即。在运行时,jit 编译器会做一些聪明的事情。

如果您调试相同的构建 jit 优化将被关闭。因此jit编译器会生成不同的机器码指令。

优化各不相同。一个例子是变量的存储。变量存储在寄存器中,并非所有寄存器的大小都相同。如果代码经过优化,一些步骤可能会被删除或按顺序打乱。因此,给定操作的寄存器选择可能会改变。因此,存储值的准确性会发生变化。

这导致浮点计算的输出不同。

编译器通常保证中间步骤的最低准确度,但很少保证最高准确度。

另见CLR JIT optimizations violates causality?

【讨论】:

  • 您知道一种通过完全 JIT 优化将结果打印到控制台窗口的方法吗?也许我应该创建一个文本文件,以便不再涉及 Visual Studio?
  • 我想你可能要求的是不可能的。我认为您要求使用或不使用日志记录的相同浮点结果?添加任何类型的日志记录都会更改代码,因此有可能更改浮点输出。您很可能需要查看评估日志输出的容差。
【解决方案4】:

即使是 Visual Studio Ctrl+F5F5 也会产生不同的浮点值。打印准确值的唯一选择是在 Release 模式下运行且不使用 Visual Studio (Ctrl+F5) 时从您的代码创建一个文本文件。不同的机器会产生不同的浮点值,所以由你决定在哪里生成它。

这样你所有的浮点数都会完全匹配!

【讨论】:

    猜你喜欢
    • 2017-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多