【问题标题】:Does the compiler optimizes repeated identical checks编译器是否优化重复的相同检查
【发布时间】:2014-10-28 01:28:25
【问题描述】:

我遇到过一些重复检查相同条件的代码。似乎 C# 6 将使我们摆脱这种丑陋的冗余代码,但与此同时,引入 bool 变量是否有任何好处,或者编译器是否足够聪明,可以为我们解决这个问题 而不是一遍又一遍地比较同样的事情? (即使我们无论如何都要进行检查,我会假设将结果存储在 bool 中会(略微)更快?)

// here we're doing the same check over and over again
string str1 = (CustomerData == null) ? string.Empty : CustomerData.str1;
string str2 = (CustomerData == null) ? string.Empty : CustomerData.str2;
string str3 = (CustomerData == null) ? string.Empty : CustomerData.str3;
// ... rinse and repeat

// here we're still doing a check, but against a boolean variable
bool is_valid  = CustomerData == null;
string str1 = is_valid ? string.Empty : CustomerData.str1;
string str2 = is_valid ? string.Empty : CustomerData.str2;
string str3 = is_valid ? string.Empty : CustomerData.str3;
// ... rinse and repeat

在这种情况下,这可能并不重要,但如果要比较两个对象,然后需要深入检查其中的所有字段,会发生什么?

注意:由于这是在方法内部,我不能依赖字符串的默认值 (null),因此解决方法是创建所有字符串,并将它们初始化为 string.Empty,然后执行以下操作:

if (CustomerData != null) {
    // set all of the above strings again, changing from empty to actual values  
}

【问题讨论】:

  • 旁注:null-objects 通常可以解决特定类型的检查:CustomerData = CustomerData ? Data.Default;(其中Data.Default 的所有“空”字段都填充了安全值)。
  • 是的,null-objects 很酷,也应该有助于进行一些测试。将无法将它们实施到我当前的项目中,因为它太大了。因此,上面的问题:)

标签: c# performance compiler-optimization


【解决方案1】:

为了扩展 codenheim 的答案.. 看来,在 Release 版本中,JITter 足够聪明,可以将它们优化掉。

Debug 版本会进行所有的比较,并且会跳来跳去。发布版本(无论如何都在 x64 上..)产生:

; string str1 = (CustomerData == null) ? string.Empty : CustomerData.str1;
call        000000005F64D620 
mov         rdx,0E7A80733A0h 
mov         rdx,qword ptr [rdx] 
lea         rdi,[rbp+10h] 
mov         rcx,rdi 
call        000000005F64D620 
mov         rdx,0E7A80733A8h 
mov         rdx,qword ptr [rdx] 
lea         rbx,[rbp+18h] 
mov         rcx,rbx 
call        000000005F64D620 
mov         rsi,qword ptr [rsi] 
; string str2 = (CustomerData == null) ? string.Empty : CustomerData.str2;
mov         rdi,qword ptr [rdi] 
; string str3 = (CustomerData == null) ? string.Empty : CustomerData.str3;
mov         rbx,qword ptr [rbx] 
; string str6 = is_valid ? string.Empty : CustomerData.str3;
mov         rbp,qword ptr [rbp+18h] 

似乎它只是忽略了您的代码,然后将数据移动到它知道应该在的位置.. 鉴于之前评估的相同表达式的结果在那时是已知的及时。

【讨论】:

  • 很好,很高兴知道。我几乎在回答中多走了一步,但太懒了。感谢您的跟进。
【解决方案2】:

我想我们必须具体说明哪个编译器。有两种编译器正在考虑中,C#(源 -> MSIL)和 JITter(MSIL -> 本机)

不,Microsoft C# 编译器不会重写此代码来优化多重检查。根据我的经验,C# 编译器几乎没有优化(出于某种原因),而 MSIL 相当于传统编译器链中的中间代码。

C# 代码...

Customer CustomerData = new Customer();

string str1 = (CustomerData == null) ? string.Empty : CustomerData.str1;
string str2 = (CustomerData == null) ? string.Empty : CustomerData.str2;
string str3 = (CustomerData == null) ? string.Empty : CustomerData.str3;

在发布模式下编译为 MSIL

IL_0006:  ldloc.0               // load CustomerData
IL_0007:  brfalse.s  IL_0012    // if(CustomerData == ) ...

IL_0009:  ldloc.0               // load CustomerData
IL_000a:  ldfld      string ConsoleApplication1.Customer::str1
IL_000f:  pop
IL_0010:  br.s       IL_0018

IL_0012:  ldsfld     string [mscorlib]System.String::Empty
IL_0017:  pop
IL_0018:  ldloc.0               // load CustomerData
IL_0019:  brfalse.s  IL_0024    // if(CustomerData == null) ...

IL_001b:  ldloc.0               // load CustomerData
IL_001c:  ldfld      string ConsoleApplication1.Customer::str2
IL_0021:  pop
IL_0022:  br.s       IL_002a

IL_0024:  ldsfld     string [mscorlib]System.String::Empty
IL_0029:  pop
IL_002a:  ldloc.0               // load CustomerData
IL_002b:  brfalse.s  IL_0036    // if(CustomerData == null) ...

IL_002d:  ldloc.0               // load CustomerData
IL_002e:  ldfld      string ConsoleApplication1.Customer::str3
IL_0033:  pop
IL_0034:  br.s       IL_003c

至于临时变量是否效果更好,归根结底是哪个操作更快:

ldloc

ldsfld

本地更快,但如果 JITter 碰巧将其中任何一个存储在寄存器中,则不会有任何区别。

请记住,MSIL 可以很好地了解正在发生的事情,但这并不意味着 JITter 不会做更多优化(我认为我们可以假设它实际上做了更多)所以看看我们会需要转储 x86 代码..

参见第 2 部分 - SimonWhitehead (+1) 转储了 x86/64 原生结果,我们发现 JITter 不仅仅是一个漂亮的翻译引擎名称 - https://stackoverflow.com/a/26600198/257090

对于它的价值,无论哪种方式,我都不会为此失眠,性能开销可以忽略不计(每个字段 2 个操作码),只要保持条件不变,它会使代码更清晰。

【讨论】:

  • 发布版本中的 JITter 似乎会将其优化掉。
  • @SimonWhitehead 谢谢!我将修改我的答案,尽管它仍然是正确的,具体取决于我们正在谈论的编译器。 C# 编译器不会这样做,JITter 显然会这样做。
  • 确实如此。也为您的细分+1 :)
  • 已修订,并链接到您对“第 2 部分”的答案!
猜你喜欢
  • 2017-04-21
  • 1970-01-01
  • 2017-06-26
  • 2016-04-18
  • 1970-01-01
  • 1970-01-01
  • 2013-08-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多