调用空Action会对性能产生什么影响?

时间:2015-11-02 06:21:40

标签: c#

假设我们有这样一个类:

public class Task
{
      public Action PostAction=()=>{}

      public void Do()
      {
             //Do Some Stuffs
             PostAction();
      }
}

正如您所见,PostAction就在那里,所以我们可以注入一个帖子。

现在假设我们有一个任务列表,其中一些人有一个帖子,其中大部分都没有,我们这样称呼他们:

foreach(var task in tasks)
      task.Do();

空帖子操作是否对上述代码的执行有任何性能影响?

1 个答案:

答案 0 :(得分:5)

这经常出现,因为你的代码也是在你举起一个事件时必须做空测试的黑客。抖动优化器无法做任何事情来优化do-nothing调用,它不能超出委托调用。此代码的快速版本避免了委托调用:

public class Task {
      public Action PostAction;

      public void Do() {
          if (PostAction != null) PostAction();
      }
}

使用System.Diagnostics.Stopwatch分析差异非常棘手,委托调用速度非常快。我确保通过给它[Me​​thodImpl(MethodImplOptions.Noinlining)]属性来内联Do()调用。使用移动Haswell芯片的非常快的笔记本电脑的平均测量值:

  • Empty Do():3.9 nanoseconds / call
  • 空测试:4.3纳秒/呼叫= 4.3 - 3.9~ = 0.4纳秒
  • 调用:7.5纳秒/调用= 7.5 - 3.9~ = 3.6纳秒

请注意" Empty Do()"测量包括执行测试的开销,for(;;)循环以及进行方法调用。这是所有测量中不变的开销。

你可以告诉费用,委托调用比null测试慢(3.6 - 0.4)/ 0.4~ = 800%。如果你假设恒定开销具有代表性(它不是),你可以得到更友好的数字:( 7.5 - 4.3)/ 4.3~ = 75%慢。您的foreach循环将有更多的开销,从而降低了穿孔损失百分比。使用带有foreach的Enumerable.Range进行迭代测试(17.0 - 13.9)/ 13.9~ = 22%慢。

当然,这些数字相当可观。但是,绝对数字非常低。如果你不经常给代表打电话,或者有很多//Do Some Stuffs(从而增加了不断的开销),那么很难注意到性能损失。

测试null,如演示如何引发事件的任何示例代码所示,是推荐的方法。如果你非常期望代表将被替换(即事件将被订阅),你只有充分的理由使用lambda。在这种情况下,您优化了空检查,每次调用节省大约一个CPU周期。