没有调试的C#64位发布版本与调试启动时的行为不同(BigInteger)

Phi*_*ges 9 c# 64-bit biginteger

我是C#的新手并且遇到了以下代码的问题(我的目标框架为4.5,我添加了对System.Numerics的引用):

using System;
using System.Numerics;

namespace Test
{
    class Program
    {
        static BigInteger Gcd(BigInteger x, BigInteger y)
        {
            Console.WriteLine("GCD {0}, {1}", x, y);
            if (x < y) return Gcd(y, x);
            if (x % y == 0) return y;
            return Gcd(y, x % y);
        }

        static void Main(string[] args)
        {
            BigInteger a = 13394673;
            BigInteger b = 53578691;
            Gcd(a, b);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当通过调试启动发布版本时(Visual Studio中的F5 - 以及程序结束时的断点,以便我可以看到输出),我得到以下输出:

GCD 13394673, 53578691
GCD 53578691, 13394673
GCD 13394673, 13394672
GCD 13394672, 1
Run Code Online (Sandbox Code Playgroud)

但是,在没有调试(Ctrl-F5)的情况下启动发布版本时,我得到以下内容:

GCD 13394673, 53578691
GCD 53578691, 53578691
Run Code Online (Sandbox Code Playgroud)

奇怪的是,如果我在程序的末尾添加一个Console.ReadLine(),它会按预期工作!

是什么原因引起了这个?谢谢.

Han*_*ant 11

这是.NET 4.0到4.5.2中的x64抖动优化器错误.表征它是相当困难的,由于BigInteger的使用,codegen非常繁重.x64抖动有结构类型的优化程序错误的历史,比如BigInteger,所以这可能是潜在的原因.与此方法中可能的尾调用优化的组合是最可能的触发器.

我通常会建议报告这样的错误,但这种抖动的日子已经过了.微软决定退休并完全重写它.在.NET 4.6 - VS2015中可用,项目代码名称为RyuJIT.它没有这个bug.

几种可能的解决方法:

Project + Properties,Build选项卡,Platform target = x86.这迫使程序以32位模式运行并使用x86抖动,它没有这个bug.

或者使用以下属性禁用此方法的优化:

  using System.Runtime.CompilerServices;
  ...
    [MethodImpl(MethodImplOptions.NoOptimization)]
    static BigInteger Gcd(BigInteger x, BigInteger y) {
        // etc...
    }
Run Code Online (Sandbox Code Playgroud)

哪个很好,繁重的工作在BigInteger类中,因此禁用优化不会影响执行时间.