c# – bool检查比null检查更快吗?
例如.我需要提取吗?
bool xIsNull = x == null 从我检查x == null的循环? 据我所知,如果(a == true)和if(x == null)都使用相同的IL指令.但指针由32位或64位组成. CLR应该检查每一位与null进行比较吗? UPDATE UPDATE2 解决方法
记住“过早优化是所有邪恶的根源”,优化的第一条规则是“不要”(第二条,仅针对专业人士,是“不要做它”),这就是发生的事情.
TL; DR 考虑以下代码: string x = null; bool a = x == null; if ( a == true ) { Console.WriteLine( ); } if ( x == null ) { Console.WriteLine( ); } 这是在调试模式下生成的IL(我添加了一些注释): .method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 46 (0x2e) .maxstack 2 .locals init ([0] string x,[1] bool a,[2] bool CS$4$0000) IL_0000: nop IL_0001: ldnull IL_0002: stloc.0 // string x = null IL_0003: ldloc.0 IL_0004: ldnull IL_0005: ceq // compare x and null IL_0007: stloc.1 // and store the result in a IL_0008: ldloc.1 IL_0009: ldc.i4.0 IL_000a: ceq IL_000c: stloc.2 // compare a and false IL_000d: ldloc.2 IL_000e: brtrue.s IL_0018 // if true (that is,a is false),skip IL_0010: nop IL_0011: call void [mscorlib]System.Console::WriteLine() IL_0016: nop IL_0017: nop IL_0018: ldloc.0 IL_0019: ldnull IL_001a: ceq // compare x and null IL_001c: ldc.i4.0 IL_001d: ceq // and compare with false IL_001f: stloc.2 IL_0020: ldloc.2 IL_0021: brtrue.s IL_002b // if true (that is,x == null),skip IL_0023: nop IL_0024: call void [mscorlib]System.Console::WriteLine() IL_0029: nop IL_002a: nop IL_002b: br.s IL_002d IL_002d: ret } // end of method Program::Main 总的来说,有很多ldloc和stloc可以读写内存数据;它们非常有用,可以帮助调试器.但是你可以看到有一个隐藏的局部变量,它具有与a完全相同的功能:所以如果你不使用临时变量,编译器就会为你使用它.还要注意使用泛型null. 现在这里是启用了优化的Release IL: .method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 24 (0x18) .maxstack 2 .locals init ([0] string x,[1] bool a) IL_0000: ldnull IL_0001: stloc.0 // set x to null IL_0002: ldloc.0 IL_0003: ldnull IL_0004: ceq IL_0006: stloc.1 // bool a = x == null IL_0007: ldloc.1 IL_0008: brfalse.s IL_000f // if false skip IL_000a: call void [mscorlib]System.Console::WriteLine() IL_000f: ldloc.0 IL_0010: brtrue.s IL_0017 // if true (so x != null) skip IL_0012: call void [mscorlib]System.Console::WriteLine() IL_0017: ret } // end of method Program::Main 在优化版本中,编译器不执行显式比较,也不使用临时变量.尽管如此,在未经优化的版本中,它会存储并在此之后加载它以检查条件;这是因为stloc弹出一个堆栈,所以它必须再次推送它. 现在,让我们比较JITter生成的代码(我设置x = Console.Readline()以防止整个代码被优化掉).这适用于调试配置(如Visual Studio中所示): string x = null; 00000043 xor edx,edx 00000045 mov dword ptr [ebp-40h],edx bool a = x == null; 00000048 cmp dword ptr [ebp-40h],0 0000004c sete al 0000004f movzx eax,al 00000052 mov dword ptr [ebp-44h],eax if ( a == true ) { Console.WriteLine( ); } 00000055 cmp dword ptr [ebp-44h],0 00000059 sete al 0000005c movzx eax,al 0000005f mov dword ptr [ebp-48h],eax 00000062 cmp dword ptr [ebp-48h],0 00000066 jne 00000070 00000068 nop 00000069 call 6027B57C 0000006e nop 0000006f nop if ( x == null ) { Console.WriteLine( ); } 00000054 cmp dword ptr [ebp-0Ch],0 00000058 jne 00000065 0000005a mov ecx,dword ptr ds:[0350208Ch] 00000060 call 602DD5E0 return; 00000065 nop 00000066 mov esp,ebp 00000068 pop ebp 00000069 ret 正如您所看到的,此代码紧跟相应的未优化IL,并在检查a的条件时使用临时变量.另一方面,由于null在我的机器上实现为0,因此比较x和null更快. 这是通过OllyDbg看到的发布代码: string x = Console.ReadLine( ); 002F0075 E8 EA808A60 CALL mscorlib_ni.60B98164 002F007A 8BC8 MOV ECX,EAX 002F007C 8B01 MOV EAX,DWORD PTR DS:[ECX] 002F007E 8B40 2C MOV EAX,DWORD PTR DS:[EAX+2C] 002F0081 FF50 1C CALL DWORD PTR DS:[EAX+1C] bool a = x == null; 002F0084 8BF0 MOV ESI,EAX 002F0086 85F6 TEST ESI,ESI 002F0088 0F94C0 SETE AL 002F008B 0FB6C0 MOVZX EAX,AL 002F008E 8BF8 MOV EDI,EAX Systed.Diagnostics.Debugger.Break( ); 002F0090 E8 E37C8E60 CALL mscorlib_ni.60BD7D78 if ( a == true ) { Console.ReadLine( ); } 002F0095 85FF TEST EDI,EDI 002F0097 74 0E JE SHORT 002F00A7 002F0099 E8 A6F92D60 CALL mscorlib_ni.605CFA44 002F009E 8BC8 MOV ECX,EAX 002F00A0 8B01 MOV EAX,DWORD PTR DS:[ECX] 002F00A2 8B40 38 MOV EAX,DWORD PTR DS:[EAX+38] 002F00A5 FF10 CALL DWORD PTR DS:[EAX] if ( x == null ) { Console.ReadLine( ); } 002F00A7 85F6 TEST ESI,ESI 002F00A9 75 0E JNE SHORT 002F00B9 002F00AB E8 94F92D60 CALL mscorlib_ni.605CFA44 002F00B0 8BC8 MOV ECX,EAX 002F00B2 8B01 MOV EAX,DWORD PTR DS:[ECX] 002F00B4 8B40 38 MOV EAX,DWORD PTR DS:[EAX+38] 002F00B7 FF10 CALL DWORD PTR DS:[EAX] return; 002F00B9 5E POP ESI 002F00BA 5F POP EDI 002F00BB 5D POP EBP 002F00BC C3 RETN 在这段代码中,a保存在edi中,x保存在esi中,并且有一些调用mscorlib来检索指向ReadLine和WriteLine的指针.话虽如此,这两种方法实际上存在差异;在将x与null(test esi,esi)进行比较之后,结果从零标志移动到al(sete al),然后扩展到整个eax(movzx eax,al). 所以,即使在如此简单的情况下,JITter也没有做好工作;因此,如果没有临时变量,您可以获得较小的性能提升. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |