首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在64位以下缓慢执行。可能是RyuJIT错误吗?

在64位以下缓慢执行。可能是RyuJIT错误吗?
EN

Stack Overflow用户
提问于 2015-11-18 07:49:24
回答 2查看 232关注 0票数 6

下面的C#代码试图在发布模式下进行基准测试:

代码语言:javascript
复制
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication54
{
class Program
{
    static void Main(string[] args)
    {
        int counter = 0;
        var sw = new Stopwatch();
        unchecked
        {
            int sum = 0;
            while (true)
            {
                try
                {
                    if (counter > 20)
                        throw new Exception("exception");
                }
                catch
                {
                }

                sw.Restart();
                for (int i = 0; i < int.MaxValue; i++)
                {
                    sum += i;
                }
                counter++;
                Console.WriteLine(sw.Elapsed);
            }

        }
    }
}
}

我是在64位的机器和VS 2015安装。当我在32位下运行代码时,它在0.6秒的上运行每次迭代,并打印到控制台上。当我在64位下运行它时,每次迭代的持续时间就会跳到,4秒,!我在我的同事计算机上试用了示例代码,它只安装了VS 2013。32位和64位版本都运行在0.6秒左右.

此外,如果我们只是删除try catch块,它也在0.6秒中运行,VS 2015为64位。

当存在try catch块时,这看起来像是一个严重的RyuJIT回归。我说得对吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-11-18 08:22:19

板凳标记是一门很好的艺术。对代码做一个小修改:

代码语言:javascript
复制
   Console.WriteLine("{0}", sw.Elapsed, sum);

现在你会看到这种差别消失了。换句话说,x86版本现在和x64代码一样慢。您可能会发现,RyuJIT没有做什么,遗留抖动做了什么,从这个小变化,它没有消除不必要的

代码语言:javascript
复制
   sum += i;

当您查看使用Debug > Windows >反汇编生成的机器代码时,您可以看到一些内容。这确实是RyuJIT的一个怪癖。它的死代码消除没有遗留抖动那么彻底。否则,并非完全没有理由,微软重写了x64抖动,因为它不容易修复的错误。其中之一对优化器来说是一个相当棘手的问题,它在优化方法上所花费的时间没有上限。在具有非常大的主体的方法上造成相当糟糕的行为,它可能会在森林中运行数十毫秒,并导致明显的执行暂停。

把它叫做窃听器,嗯,不完全是。写出理智的代码,抖动不会让你失望。优化永远从通常的地方开始,在程序员的耳朵之间。

票数 11
EN

Stack Overflow用户

发布于 2015-11-18 09:12:45

经过一些测试,我得到了一些有趣的结果。我的测试围绕着try catch块进行。正如OP所指出的,如果删除这个块,那么执行的时间是相同的。我进一步缩小了范围,并得出结论,这是因为counter语句中的if变量在try块中。

让我们移除冗余的throw

代码语言:javascript
复制
                try
                {
                    if (counter== 0) { }
                }
                catch
                {
                }

您将获得与原始代码相同的结果。

让我们将计数器更改为实际的int值:

代码语言:javascript
复制
                try
                {
                    if (1 == 0) { }
                }
                catch
                {
                }

使用此代码,64位版本的执行时间已从4秒缩短到大约1.7秒。仍然是32位版本的两倍。不过,我觉得这很有趣。不幸的是,在我快速搜索Google之后,我还没有想出原因,但是如果我知道为什么会这样的话,我会进一步挖掘并更新这个答案。

至于我们想要删除64位版本的剩余秒,我可以看到,这取决于在您的sum循环中通过i增量for。让我们对此进行更改,这样sum就不会超出其界限:

代码语言:javascript
复制
            for (int i = 0; i < int.MaxValue; i++)
            {
                sum ++;
            }

此更改(以及try块中的更改)将将64位应用程序的执行时间缩短到0.7秒。我对1秒时间差的推理是因为64位版本需要处理int (自然是32位)的人为方式。

在32位版本中,有32位分配给Int32 (sum)。当sum超出其界限时,很容易确定这一事实。

在64位版本中,有64位分配给Int32 (sum)。当sum超出其界限时,需要有一种机制来检测这一点,这可能会导致减速。也许,由于分配的冗余位数增加,添加sum & i的操作也要花费更长的时间。

我是在这里建立理论,所以不要把这当作福音。我只是想贴出我的发现。我相信其他人会对我发现的问题有所了解。

--

更新

@HansPassant的回答指出,sum += i;行可能被删除,因为它被认为是不必要的,这是完全合理的,sum没有在for循环之外使用。在他将for循环外的和值引入之后,我们注意到x86版本和x64版本一样慢。所以我决定做一些测试。让我们将for循环和打印改为如下:

代码语言:javascript
复制
                int x = 0;
                for (int i = 0; i < int.MaxValue; i++)
                {
                    sum += i;
                    x = sum;
                }
                counter++;
                Console.WriteLine(sw.Elapsed + "  " +  x);

您可以看到,我引入了一个新的int x,它被分配给for循环中的sum值。没有将x的值写入控制台。sum没有离开for循环。信不信由你,这实际上将x64的执行时间缩短到了0.7秒。但是,x86版本可跳转到1.4秒。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33774393

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档