我遇到了一些反复检查相同条件的代码。好像C#6会让我们摆脱这个丑陋的冗余代码,但与此同时,引入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
在这种情况下,这可能并不重要,但如果比较2个需要去的对象并深入检查其中的所有字段会发生什么?
注意:由于这是在方法内,我不能依赖字符串的默认值(null
),因此解决方法是创建所有字符串,将它们初始化为
string.Empty,然后做类似的事情:
if (CustomerData != null) {
// set all of the above strings again, changing from empty to actual values
}
答案 0 :(得分:6)
要扩展codenheim的答案......看来,在发布版本中,JITter非常智能,可以优化它们。
Debug构建会进行所有比较并跳转很多。 Release版本(无论如何在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]
它似乎只是忽略了你的代码然后将数据移动到知道它应该的位置......给定之前已经评估过的相同表达式的结果是已知的及时。
答案 1 :(得分:4)
我想我们必须具体说明哪个编译器。正在考虑两个编译器,C#(source - > MSIL)和JITter(MSIL - > native)
不,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个操作码),只是保持条件不变,它使代码更清洁