此代码为两个方法的 n 调用提供时间安排,这两个方法都不执行任何操作(MyMethod1
,MyMethod2
),但使用不同的属性(MyAspect1
进行修饰, MyAspect2
)。
MyAspect1
运行(MyMethod1
)之外, Proceed()
不包含任何逻辑。
MyAspect2
对MyMethod2
执行相同的操作,但也会创建一个Task,提供一个匿名委托来运行,这意味着等同于CPU的近零工作(修剪字符串)。 / p>
我希望MyMethod1
和MyMethod2
上的迭代时间大致相同。
实际时间如下;它们似乎是反直觉的,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
答案 0 :(得分:4)
好吧,首先,我会尝试确定这是否与PostSharp有关。我会尝试调用相同的方法,而不涉及任何AOP。
这是我的怀疑:
25000个刻度(很短的时间,请注意)是由于PostSharp基础设施的JITting位。因此,它们是不变的成本,不必须通过第二种方法再次支付。您可以通过切换测试顺序来验证这一点。但是,考虑到数字,我怀疑通过设置任务工厂等会产生另一个常数成本......这可以解释为什么方法2的100次迭代不会花费比5次迭代更长的时间。
创建任务不是免费的,我不希望这样。只是你正在创建一个对象的事实意味着正在进行的工作......然后是跨任务和线程的调度和协调。并行扩展是好的,但它不能创造奇迹。我不知道PFX将如何推广工作,但是有可能生成一些额外的工作(任务很短,但仍然必须执行)最终与主线程在同一个核心上。你很快就会创造很多任务。
这两种方法之间的差异可能足以改变它们是否可以内联。使用PostSharp对此有些模糊,但我发现情况并非如此。
在方法2的情况下,缩放不是很线性,但它并不遥远。如果你采用6000 + 23 * N
的公式作为一个快速和肮脏的近似值,你会期望值为6115,8300,29000,236000和23006000 - 这些非常远远不是。
我强烈怀疑上次测试中第一个方法的巨大数字是由于垃圾收集和堆增长。你在这里创建了大量的对象,我怀疑它正在做一个完整的GC并适当地扩展各代 - 同一测试中的方法2然后利用了更大的堆。
这些只是猜测,但它们为你所看到的一切提供了某种解释:)