为什么 Console.WriteLine 会加速我的应用程序?

3
好的,这有点奇怪。我有一个算法来找到两个因子都有K位数字的最高可能数值回文。
我用的方法是查找数字集的最高可能回文(例如,如果k=3,则最高可能为999999,然后是998899等)。然后,我检查该回文是否具有两个K位数字的因子。
为了调试,我认为将我正在检查的每个回文打印到控制台是个好主意(以确保我得到了所有回文)。令我惊讶的是,添加
Console.WriteLine(palindrome.ToString());

每次寻找回文数的迭代都能将我的运行时间从约24秒降低到约14秒。

为了验证,我多次运行了程序,然后注释掉了Console命令并多次运行,每次都比有Console命令时更短。

这看起来很奇怪,有什么想法吗?

如果有人想尝试,请参考以下源代码:

    static double GetHighestPalindromeBench(int k)
    {
        //Because the result of k == 1 is a known quantity, and results in aberrant behavior in the algorithm, handle as separate case
        if (k == 1)
        {
            return 9;
        }


        /////////////////////////////////////
        //These variables will be used in HasKDigitFactors(), no need to reprocess them each time the function is called
        double kTotalSpace = 10;

        for (int i = 1; i < k; i++)
        {
            kTotalSpace *= 10;
        }

        double digitConstant = kTotalSpace;  //digitConstant is used in HasKDigits() to determine if a factor has the right number of digits

        double kFloor = kTotalSpace / 10; //kFloor is the lowest number that has k digits (e.g. k = 5, kFloor = 10000)

        double digitConstantFloor = kFloor - digitConstant;  //also used in HasKDigits()

        kTotalSpace--;  //kTotalSpace is the highest number that has k digits (e.g. k = 5, kTotalSpace = 99999)

        /////////////////////////////////////////


        double totalSpace = 10;

        double halfSpace = 10;

        int reversionConstant = k;

        for (int i = 1; i < k * 2; i++)
        {
            totalSpace *= 10;
        }

        double floor = totalSpace / 100;

        totalSpace--;


        for (int i = 1; i < k; i++)
        {
            halfSpace *= 10;
        }

        double halfSpaceFloor = halfSpace / 10;    //10000

        double halfSpaceStart = halfSpace - 1;     //99999

        for (double i = halfSpaceStart; i > halfSpaceFloor; i--)
        {
            double value = i;
            double palindrome = i;
            //First generate the full palindrome
            for (int j = 0; j < reversionConstant; j++)
            {
                int digit = (int)value % 10;

                palindrome = palindrome * 10 + digit;

                value = value / 10;
            }

            Console.WriteLine(palindrome.ToString());
            //palindrome should be ready
            //Now we check the factors of the palindrome to see if they match k
            //We only need to check possible factors between our k floor and ceiling, other factors do not solve
            if (HasKDigitFactors(palindrome, kTotalSpace, digitConstant, kFloor, digitConstantFloor))
            {
                return palindrome;
            }
        }


        return 0;
    }

    static bool HasKDigitFactors(double palindrome, double totalSpace, double digitConstant, double floor, double digitConstantFloor)
    {
        for (double i = floor; i <= totalSpace; i++)
        {
            if (palindrome % i == 0)
            {
                double factor = palindrome / i;
                if (HasKDigits(factor, digitConstant, digitConstantFloor))
                {
                    return true;
                }
            }
        }
        return false;
    }

    static bool HasKDigits(double value, double digitConstant, double digitConstantFloor)
    {
        //if (Math.Floor(Math.Log10(value) + 1) == k)
        //{
        //    return true;
        //}
        if (value - digitConstant > digitConstantFloor && value - digitConstant < 0)
        {
            return true;
        }

        return false;
    }

请注意,我在HasKDigits函数中把Math.Floor操作注释掉了。这是因为我当时想确定我的数字检查操作是否比Math.Floor操作更快。谢谢!
编辑:函数调用
我正在使用StopWatch来测量处理时间。我还使用了一个物理秒表来验证StopWatch的结果。
        Stopwatch stopWatch = new Stopwatch();
        stopWatch.Start();
        double palindrome = GetHighestPalindromeBench(6);
        stopWatch.Stop();

        TimeSpan ts = stopWatch.Elapsed;

        string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}:{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10);

        Console.WriteLine();
        Console.WriteLine(palindrome.ToString());
        Console.WriteLine();
        Console.WriteLine(elapsedTime);

1
你是如何衡量的? - Ry-
2
两者都是在Debug模式还是Release模式下运行的?.NET版本是多少?你是在哪里进行测量的?你没有展示测量的代码。 - quantdev
1
这真的是一个大猜测:也许是因为你正在向IO写入,所以你的线程获得了更多的处理器时间,因为WriteLine是同步的,从而阻塞了其他线程。 - ForguesR
你在所有情况下都有相同的函数结果吗?这个程序中还有其他线程在运行吗? - quantdev
@Tevis 在 VS 2013 和 .net 4.5 中几乎有相同的结果。 - LVBen
显示剩余14条评论
1个回答

4
我已经测试了你的代码。我的系统是i7-3770 3.40 GHz,四核心带超线程,因此有8个核心可用。
调试版本,带或不带控制台Writeline语句(注释掉或未注释),在调试模式或非调试模式下,时间从约8.7秒到9.8秒不等。作为发布版本,无论如何都会降至约6.8-7.0秒。这些数字在VS内部和命令行中相同。因此,你的观察结果无法重现。
在没有控制台输出的性能监视器上,我看到一个核心使用率达到100%,但它在1、4、5和8之间切换。没有控制台输出时,其他核心也有活动。最大CPU使用率从未超过18%。
在我看来,你的控制台输出值可能与我的一致,并代表着真实值。因此,你的问题应该是:为什么你的系统在不进行控制台输出时速度那么慢?
答案是:因为你的计算机或项目存在某些我们不知道的差异。我以前从未见过这种情况,但是某些事情正在吸收资源,你应该能够找出它是什么。
尽管这不是一个真正的答案,但我已将其写作答案。如果你获得更多的事实并更新你的问题,希望我能提供更好的答案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接