内部的"If(.. || ..)"和"If(... && ...)"构造中发生了什么?

Pix*_*ulp 21 c# if-statement

我只是想知道" if OR "和" if AND " 内部会发生什么.我有一种感觉,它只是使用语法糖&&,||并且在内部所有情况都构建为单个if语句.

紧凑形式||:

if(a || b || c)
{
    DoSomething();
}
Run Code Online (Sandbox Code Playgroud)

潜在的内部形式:

if(a)
{
    DoSomething();
}    
else if(b)
{
    DoSomething();
}    
else if(c)
{
    DoSomething();
}
Run Code Online (Sandbox Code Playgroud)

紧凑形式&&:

if(a && b && c)
{
    DoSomething();
}
Run Code Online (Sandbox Code Playgroud)

潜在的内部形式:

if(a)
{
    if(b)
    {
        if(c)
        {
            DoSomething();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这两个例子的表现有什么不同吗?

*编辑:将其他内容添加到|| 案件

Tho*_*oub 33

首先,||并且&&短路的.这意味着:

if(a || b || c)
    DoSomething();
Run Code Online (Sandbox Code Playgroud)

如果a是真实的,bc不会进行评估.

其次,你的实现||是假的:

if(a)
    DoSomething();
if(b)
    DoSomething();
if(c)
    DoSomething();
Run Code Online (Sandbox Code Playgroud)

DoSomething()最多会被召唤3次.

它应该是:

if(a)
    DoSomething();
else if(b)
    DoSomething();
else if(c)
    DoSomething();
Run Code Online (Sandbox Code Playgroud)

要完成,如果你想表现先喜欢短呼叫您的条件:

if(aShortFunctionToExecute() || aVeryVeryLongFunctionToExecute())
     DoSomething();
Run Code Online (Sandbox Code Playgroud)

会比快

if(aVeryVeryLongFunctionToExecute() || aShortFunctionToExecute())
     DoSomething();
Run Code Online (Sandbox Code Playgroud)

因为懒惰的评价


如果您反汇编代码:

private static void Main()
{
    if (a() && b() && c())
    {
        Console.WriteLine("DoSomething");
    }
}
bool a(){
    return true;
}
bool b(){
    return 3 % 2 == 1;
}
bool c(){
    return (3 % 2) / 1 == 1;
}
Run Code Online (Sandbox Code Playgroud)

你会得到:

    if (a() && b() && c())
00000022  call        FFFFFFFFFFEE8D90 
00000027  mov         byte ptr [rbp+20h],al 
0000002a  movzx       eax,byte ptr [rbp+20h] 
0000002e  test        eax,eax 
00000030  je          000000000000005A 
00000032  call        FFFFFFFFFFEE8D98 
00000037  mov         byte ptr [rbp+21h],al 
0000003a  movzx       eax,byte ptr [rbp+21h] 
0000003e  test        eax,eax 
00000040  je          000000000000005A 
00000042  call        FFFFFFFFFFEE8DA0 
00000047  mov         byte ptr [rbp+22h],al 
0000004a  movzx       ecx,byte ptr [rbp+22h] 
0000004e  xor         eax,eax 
00000050  test        ecx,ecx 
00000052  sete        al 
00000055  mov         dword ptr [rbp+24h],eax 
00000058  jmp         0000000000000062 
0000005a  nop 
0000005b  mov         dword ptr [rbp+24h],1 
00000062  nop 
00000063  movzx       eax,byte ptr [rbp+24h] 
00000067  mov         byte ptr [rbp+2Fh],al 
0000006a  movzx       eax,byte ptr [rbp+2Fh] 
0000006e  test        eax,eax 
00000070  jne         0000000000000087 
        {
00000072  nop 
            Console.WriteLine("DoSomething");
00000073  mov         rcx,12603398h 
0000007d  mov         rcx,qword ptr [rcx] 
00000080  call        00000000577A82A0 
00000085  nop 
        }
Run Code Online (Sandbox Code Playgroud)

并为代码:

private static void Main()
{
    if (a())
        if(b())
            if(c())
                Console.WriteLine("DoSomething");
}
static bool a(){
    return true;
}
static bool b(){
    return 3 % 2 == 1;
}
static bool c(){
    return (3 % 2) / 1 == 1;
}
Run Code Online (Sandbox Code Playgroud)

你会得到:

if (a())
00000022  call        FFFFFFFFFFEE8D90 
00000027  mov         byte ptr [rbp+20h],al 
0000002a  movzx       ecx,byte ptr [rbp+20h] 
0000002e  xor         eax,eax 
00000030  test        ecx,ecx 
00000032  sete        al 
00000035  mov         dword ptr [rbp+24h],eax 
00000038  movzx       eax,byte ptr [rbp+24h] 
0000003c  mov         byte ptr [rbp+3Fh],al 
0000003f  movzx       eax,byte ptr [rbp+3Fh] 
00000043  test        eax,eax 
00000045  jne         00000000000000A4 
            if(b())
00000047  call        FFFFFFFFFFEE8D98 
0000004c  mov         byte ptr [rbp+28h],al 
0000004f  movzx       ecx,byte ptr [rbp+28h] 
00000053  xor         eax,eax 
00000055  test        ecx,ecx 
00000057  sete        al 
0000005a  mov         dword ptr [rbp+2Ch],eax 
0000005d  movzx       eax,byte ptr [rbp+2Ch] 
00000061  mov         byte ptr [rbp+3Fh],al 
00000064  movzx       eax,byte ptr [rbp+3Fh] 
00000068  test        eax,eax 
0000006a  jne         00000000000000A4 
                if(c())
0000006c  call        FFFFFFFFFFEE8DA0 
00000071  mov         byte ptr [rbp+30h],al 
00000074  movzx       ecx,byte ptr [rbp+30h] 
00000078  xor         eax,eax 
0000007a  test        ecx,ecx 
0000007c  sete        al 
0000007f  mov         dword ptr [rbp+34h],eax 
00000082  movzx       eax,byte ptr [rbp+34h] 
00000086  mov         byte ptr [rbp+3Fh],al 
00000089  movzx       eax,byte ptr [rbp+3Fh] 
0000008d  test        eax,eax 
0000008f  jne         00000000000000A4 
                    Console.WriteLine("DoSomething");
00000091  mov         rcx,125D3398h 
0000009b  mov         rcx,qword ptr [rcx] 
0000009e  call        00000000577B82A0 
000000a3  nop 
Run Code Online (Sandbox Code Playgroud)

这有点长:它需要40条指令而不是31条指令.


正如thanosqr所指出的,性能还取决于您的条件成立的概率.举个例子:

如果a99%的时间都失败并且需要1秒才能运行,如果b 99%的时间成功并且需要10秒才能运行,超过100次尝试你将更快地b放在第一位:

if(b || a) => 10s 99% ==> 100 runs will take 99*10+11 = 1001s
if(b || a) => 11s 1%

if(a || b) => 11s 99% ==> 100 runs will take 99*11+1 = 1090s
if(a || b) => 1s 1%
Run Code Online (Sandbox Code Playgroud)

另外,我建议你阅读" 为什么处理排序数组比未排序数组更快? "这非常有趣!

  • 我会说性能还取决于短路的可能性; 例如:如果快速检查需要1秒并且失败99次,而100次失败,而慢速检查需要10秒但是只能失败1次,(慢||快)将需要1001秒而(快速||慢速)需要1090秒.极端的情况是检查总是假的,即使它超级快,最好先检查慢速检查. (5认同)
  • "使用紧凑型方式的速度提高了25%." 不,这不对.它减少了25%的指令.指令的数量并不表示它的运行速度,并且它会根据许多不同的参数而变化很大. (4认同)

Gen*_*ene 7

使用紧凑形式,C#编译器发出的IL将不那么冗长,导致在运行时处理的指令更少.发出的IL语句及其逻辑实际上是相同的,因此没有花哨的内置支持来处理该情况或一些特殊指令(请记住,您可以将任何带有布尔结果的表达式放入其中if).

对于使用||运算符的紧凑形式(调试版本):

.method private hidebysig static void  One() cil managed
{
  // Code size       38 (0x26)
  .maxstack  2
  .locals init ([0] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldsfld     bool ConsoleApplication4.Program::a
  IL_0006:  brtrue.s   IL_0019
  IL_0008:  ldsfld     bool ConsoleApplication4.Program::b
  IL_000d:  brtrue.s   IL_0019
  IL_000f:  ldsfld     bool ConsoleApplication4.Program::c
  IL_0014:  ldc.i4.0
  IL_0015:  ceq
  IL_0017:  br.s       IL_001a
  IL_0019:  ldc.i4.0
  IL_001a:  nop
  IL_001b:  stloc.0
  IL_001c:  ldloc.0
  IL_001d:  brtrue.s   IL_0025
  IL_001f:  call       void ConsoleApplication4.Program::DoSomething()
  IL_0024:  nop
  IL_0025:  ret
} // end of method Program::One
Run Code Online (Sandbox Code Playgroud)

使用您的内部表单(考虑您使用else if而不是if):

.method private hidebysig static void  Two() cil managed
{
  // Code size       60 (0x3c)
  .maxstack  2
  .locals init ([0] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldsfld     bool ConsoleApplication4.Program::a
  IL_0006:  ldc.i4.0
  IL_0007:  ceq
  IL_0009:  stloc.0
  IL_000a:  ldloc.0
  IL_000b:  brtrue.s   IL_0015
  IL_000d:  call       void ConsoleApplication4.Program::DoSomething()
  IL_0012:  nop
  IL_0013:  br.s       IL_003b
  IL_0015:  ldsfld     bool ConsoleApplication4.Program::b
  IL_001a:  ldc.i4.0
  IL_001b:  ceq
  IL_001d:  stloc.0
  IL_001e:  ldloc.0
  IL_001f:  brtrue.s   IL_0029
  IL_0021:  call       void ConsoleApplication4.Program::DoSomething()
  IL_0026:  nop
  IL_0027:  br.s       IL_003b
  IL_0029:  ldsfld     bool ConsoleApplication4.Program::c
  IL_002e:  ldc.i4.0
  IL_002f:  ceq
  IL_0031:  stloc.0
  IL_0032:  ldloc.0
  IL_0033:  brtrue.s   IL_003b
  IL_0035:  call       void ConsoleApplication4.Program::DoSomething()
  IL_003a:  nop
  IL_003b:  ret
} // end of method Program::Two
Run Code Online (Sandbox Code Playgroud)

因此,有更多的指令来处理附加if语句所需的所有跳转.因此,第一种形式更有效(实际上更具可读性:)).

在性能方面(每个方法使用10.000.000迭代测量10次并删除最高和最低值,发布版本):

紧凑形式:平均55ms

详细形式:平均56ms

所以没有太大的区别.