Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
415 views
in Technique[技术] by (71.8m points)

c# - 尝试抓住加速我的代码?(Try-catch speeding up my code?)

I wrote some code for testing the impact of try-catch, but seeing some surprising results.

(我写了一些代码来测试try-catch的影响,但看到了一些令人惊讶的结果。)

static void Main(string[] args)
{
    Thread.CurrentThread.Priority = ThreadPriority.Highest;
    Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;

    long start = 0, stop = 0, elapsed = 0;
    double avg = 0.0;

    long temp = Fibo(1);

    for (int i = 1; i < 100000000; i++)
    {
        start = Stopwatch.GetTimestamp();
        temp = Fibo(100);
        stop = Stopwatch.GetTimestamp();

        elapsed = stop - start;
        avg = avg + ((double)elapsed - avg) / i;
    }

    Console.WriteLine("Elapsed: " + avg);
    Console.ReadKey();
}

static long Fibo(int n)
{
    long n1 = 0, n2 = 1, fibo = 0;
    n++;

    for (int i = 1; i < n; i++)
    {
        n1 = n2;
        n2 = fibo;
        fibo = n1 + n2;
    }

    return fibo;
}

On my computer, this consistently prints out a value around 0.96..

(在我的电脑上,这始终打印出一个大约0.96的值。)

When I wrap the for loop inside Fibo() with a try-catch block like this:

(当我使用try-catch块在Fibo()中包装for循环时,如下所示:)

static long Fibo(int n)
{
    long n1 = 0, n2 = 1, fibo = 0;
    n++;

    try
    {
        for (int i = 1; i < n; i++)
        {
            n1 = n2;
            n2 = fibo;
            fibo = n1 + n2;
        }
    }
    catch {}

    return fibo;
}

Now it consistently prints out 0.69... -- it actually runs faster!

(现在它一直打印出0.69 ...... - 它实际上运行得更快!)

But why?

(但为什么?)

Note: I compiled this using the Release configuration and directly ran the EXE file (outside Visual Studio).

(注意:我使用Release配置编译它并直接运行EXE文件(在Visual Studio外部)。)

EDIT: Jon Skeet's excellent analysis shows that try-catch is somehow causing the x86 CLR to use the CPU registers in a more favorable way in this specific case (and I think we're yet to understand why).

(编辑: Jon Skeet的优秀分析表明try-catch在某种程度上导致x86 CLR在这种特定情况下以更有利的方式使用CPU寄存器(我认为我们还没理解为什么)。)

I confirmed Jon's finding that x64 CLR doesn't have this difference, and that it was faster than the x86 CLR.

(我确认Jon发现x64 CLR没有这个区别,并且它比x86 CLR更快。)

I also tested using int types inside the Fibo method instead of long types, and then the x86 CLR was as equally fast as the x64 CLR.

(我还在Fibo方法中使用int类型而不是long类型进行了测试,然后x86 CLR和x64 CLR一样快。)


UPDATE: It looks like this issue has been fixed by Roslyn.

(更新:看起来这个问题已由Roslyn修复。)

Same machine, same CLR version -- the issue remains as above when compiled with VS 2013, but the problem goes away when compiled with VS 2015.

(相同的机器,相同的CLR版本 - 在使用VS 2013编译时问题仍然如上所述,但在使用VS 2015编译时问题就消失了。)

  ask by Eren Ers?nmez translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

One of the Roslyn engineers who specializes in understanding optimization of stack usage took a look at this and reports to me that there seems to be a problem in the interaction between the way the C# compiler generates local variable stores and the way the JIT compiler does register scheduling in the corresponding x86 code.

(专门了解堆栈使用优化的Roslyn工程师之一看了一下这个并告诉我,C#编译器生成局部变量存储的方式与JIT编译器注册方式之间的交互似乎存在问题在相应的x86代码中进行调度。)

The result is suboptimal code generation on the loads and stores of the locals.

(结果是在本地的加载和存储上生成次优代码。)

For some reason unclear to all of us, the problematic code generation path is avoided when the JITter knows that the block is in a try-protected region.

(由于某些原因我们所有人都不清楚,当JITter知道该块处于try-protected区域时,可以避免有问题的代码生成路径。)

This is pretty weird.

(这很奇怪。)

We'll follow up with the JITter team and see whether we can get a bug entered so that they can fix this.

(我们将跟进JITter团队,看看我们是否可以输入错误,以便他们可以解决这个问题。)

Also, we are working on improvements for Roslyn to the C# and VB compilers' algorithms for determining when locals can be made "ephemeral" -- that is, just pushed and popped on the stack, rather than allocated a specific location on the stack for the duration of the activation.

(此外,我们正在努力改进Roslyn到C#和VB编译器的算法,以确定何时可以使本地变为“短暂” - 也就是说,只是在堆栈上推送和弹出,而不是在堆栈上分配特定位置激活的持续时间。)

We believe that the JITter will be able to do a better job of register allocation and whatnot if we give it better hints about when locals can be made "dead" earlier.

(我们相信JITter能够更好地完成寄存器分配,如果我们给出更好的提示,可以让当地人更早地“死”。)

Thanks for bringing this to our attention, and apologies for the odd behaviour.

(感谢您引起我们的注意,并为奇怪的行为道歉。)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...