【问题标题】:Performance hit of double dereferencing Reference types in C#C# 中双重取消引用引用类型的性能影响
【发布时间】:2011-12-09 17:50:39
【问题描述】:

最近,我开始从事一个项目,其中许多引用参数通过引用传递。我查看了很多代码,其中大多数方法可能会丢失ref 参数。尽管代码运行良好,但我想知道通过双重取消引用每个参数而不是仅按值传递引用可能会对性能造成什么样的影响。

例如这段代码:

public void EmptyList(ref List<string> test)
{
    test.Clear();
}

VS 这个:

public void EmptyList(List<string> test)
{
    test.Clear();
}

编辑

在阅读了 Nicholas Carey 所说的之后,我用上面的代码尝试了同样的事情。这是生成的程序集:

            EmptyList(ref List<string> test)
00000028  mov         eax,dword ptr [ebp-40h] 
0000002b  mov         ecx,dword ptr [eax] 
0000002d  cmp         dword ptr [ecx],ecx 
0000002f  call        719210C4


            EmptyList(List<string> test)
00000028  mov         ecx,dword ptr [ebp-40h] 
0000002b  cmp         dword ptr [ecx],ecx 
0000002d  call        71920FD4 

【问题讨论】:

  • 你可能不会注意到。

标签: c# pass-by-reference pass-by-value


【解决方案1】:

显式引用调用和隐式引用调用的区别在于lea(加载有效地址)指令和mov('移动')指令的区别。

AFAIK,在任何现代处理器上,这两条指令,以所使用的形式,都在一个时钟周期内执行,所以区别几乎没有实际意义。

以下是 [explicit] 引用为调用生成的代码(调试模式):

            FooBar( ref o ) ;
00000035  lea         ecx,[ebp-40h] 
00000038  call        FFD0B008 
0000003d  nop 

下面是通过 [隐式] 引用为调用生成的代码(调试模式):

            FooBar( o ) ;
0000003a  mov         ecx,dword ptr [ebp-40h] 
0000003d  call        FFD3B008 
00000042  nop 

为被调用的方法/函数生成的代码在任何一种情况下都是相同的。

这是我使用的示例代码,FWIW:

class Program
{
  static void Main( string[] args )
  {
    object o = null ;
    FooBar( ref o ) ;
  }

  private static void FooBar( ref object o )
  {
  }

}

恕我直言,除非确实需要,否则请忽略 ref 关键字:它真正所做的只是在等式中添加语义噪音。

【讨论】:

  • 您是如何获得这些装配清单的?对于这种类型的分析,我希望看到 CIL 而不是原生 x86。 (谢谢顺便说一句)
  • 设置断点。启动调试器。当您到达断点时,打开反汇编窗口:通过 Debug..Windows..Disassembly 的菜单或键盘,[CTL]+[ALT]+D 将带您到那里。
  • +1。所有的答案都是正确的,但这实际上回答了用硬数据提出的问题。
  • @NicholasCarey 有时答案太简单了!干得好。
  • 在复制您的测试并在 ref 参数的方法内完成一些工作后,汇编代码似乎显示了第二个取消引用调用。如果方法中没有使用参数,我假设编译器优化了第二次取消引用。
【解决方案2】:

性能差异可能完全不会被注意到,如果它存在的话。我个人会根据功能要求来决定是通过引用还是按值传递“参考参数”,而不是性能差异。

使用ref 传递引用类型对调用者有很大的影响。假设您的参考可能会改变。这对您的 API 的使用有明显的影响,并且强烈建议您可能正在更改值。如果不是这种情况,请按值传递引用以避免混淆,因为这将导致代码更易于维护、更易于理解。

【讨论】:

  • 谢谢,我想知道我是否可以提出论点来删除它们,而不是“它令人困惑和不必要”(这对我来说已经足够了)。
  • @scottm 从技术上讲,性能可能几乎相同。在这两种情况下,您都有效地传递了一个内存位置,无论哪种方式,这将是相同数量的复制......我怀疑您永远不会在分析器中看到差异,因为它从一个性能观点是不可检测的。
  • 混淆、不必要并且可能使代码更加脆弱。
猜你喜欢
  • 1970-01-01
  • 2013-11-26
  • 2017-01-13
  • 2014-04-20
  • 1970-01-01
  • 2015-03-24
  • 2012-04-16
  • 2017-02-10
  • 2011-01-04
相关资源
最近更新 更多