任何人都可以帮我解释这些方法时间吗?

时间:2010-07-11 17:55:03

标签: c# performance

此代码为两个方法的 n 调用提供时间安排,这两个方法都不执行任何操作(MyMethod1MyMethod2),但使用不同的属性(MyAspect1进行修饰, MyAspect2)。

除了让MyAspect1运行(MyMethod1)之外,

Proceed()不包含任何逻辑。

MyAspect2MyMethod2执行相同的操作,但也会创建一个Task,提供一个匿名委托来运行,这意味着等同于CPU的近零工作(修剪字符串)。 / p>

我希望MyMethod1MyMethod2上的迭代时间大致相同。

实际时间如下;它们似乎是反直觉的,MyMethod1开始明显变慢(与我期望的相反),并最终从10000次迭代开始显着地失去了比赛。任何人都可以帮我解释一下吗?

Test results (average of 3 runs per iteration value):

No. of iterations  |          Ticks 
                      Method 1  |  Method 2
       5                 25282        6999
       100               22128        8176 
       1000              22982        23720
       10000             25995        265621
       1000000           994359       25125076

---------------------------------------------------

public class TestClass2
{ 
    public void MyTest()
    {
        const int iterations = 100;
        var stopWatch = new Stopwatch();
        stopWatch.Start();
        for (int x = 0; x < iterations; x++)
        {
            MyMethod1();
        }
        stopWatch.Stop();
        Console.WriteLine("Method1 calls duration: " + stopWatch.ElapsedTicks);            
        stopWatch.Reset();
        stopWatch.Start();
        for (int x = 0; x < iterations; x++)
        {
             MyMethod2();
        }
        stopWatch.Stop();
        Console.WriteLine("Method2 calls duration: " + stopWatch.ElapsedTicks);                        
    }

    [MyAspect1]
    private void MyMethod1() {}
    [MyAspect2]
    private void MyMethod2() {}
}

[Serializable]
public class MyAspect1 : AsynchronousMetricsAspect
{
    public override void OnInvoke(MethodInterceptionArgs args)
    {
        args.Proceed();
    }
}

//NOTE: this type is the same as MyAspect1, but adds a Task creation
[Serializable]
public class MyAspect2 : AsynchronousMetricsAspect
{
    public override void OnInvoke(MethodInterceptionArgs args)
    {
        args.Proceed();
        Task.Factory.StartNew(() => "bleh".Trim()); //invoke the logging method asynchronously           
    }
}

更多信息:正在从ReSharper NUnit VS插件调用测试,属性为PostSharp MethodInterceptionAspect s。

Edit: Updated figures for when the methods are pre-invoked before the tests, to ensure any JIT compilation has occurred. 

Test results (ballpark):

No. of iterations  |          Ticks 
                      Method 1  |  Method 2
       5                 22          437
       100               37          2204 
       1000              192         24476
       10000             7931        286403
       100000            115451      2862439
       1000000           695950      29049021
       10000000          8347867     275777590

1 个答案:

答案 0 :(得分:4)

好吧,首先,我会尝试确定这是否与PostSharp有关。我会尝试调用相同的方法,而不涉及任何AOP。

这是我的怀疑:

  • 25000个刻度(很短的时间,请注意)是由于PostSharp基础设施的JITting位。因此,它们是不变的成本,必须通过第二种方法再次支付。您可以通过切换测试顺序来验证这一点。但是,考虑到数字,我怀疑通过设置任务工厂等会产生另一个常数成本......这可以解释为什么方法2的100次迭代不会花费比5次迭代更长的时间。

  • 创建任务不是免费的,我不希望这样。只是你正在创建一个对象的事实意味着正在进行的工作......然后是跨任务和线程的调度和协调。并行扩展是好的,但它不能创造奇迹。我不知道PFX将如何推广工作,但是有可能生成一些额外的工作(任务很短,但仍然必须执行)最终与主线程在同一个核心上。你很快就会创造很多任务。

  • 这两种方法之间的差异可能足以改变它们是否可以内联。使用PostSharp对此有些模糊,但我发现情况并非如此。

  • 在方法2的情况下,缩放不是很线性,但它并不遥远。如果你采用6000 + 23 * N的公式作为一个快速和肮脏的近似值,你会期望值为6115,8300,29000,236000和23006000 - 这些非常远远不是。

  • 我强烈怀疑上次测试中第一个方法的巨大数字是由于垃圾收集和堆增长。你在这里创建了大量的对象,我怀疑它正在做一个完整的GC并适当地扩展各代 - 同一测试中的方法2然后利用了更大的堆。

这些只是猜测,但它们为你所看到的一切提供了某种解释:)