如果 - 其他更快或? :

时间:2015-01-14 06:56:19

标签: c# performance if-statement

c#中的哪个更快

int a = 10;
bool _IsEven;
if(a%2 == 0)
{
   _IsEven = true;
}
else
{
   _IsEven = false;
}

或者

int a = 10;
bool _IsEven = a%2 == 0 ? true : false;

更新

我知道我可以在编写

时优化我的代码
bool _IsEven = a%2 == 0;

但我的问题不是关于代码优化,而是关于 这两个语句的表现 ???

你能帮助我提高我的编码知识吗?

3 个答案:

答案 0 :(得分:6)

你的第二个代码甚至不会编译。但你可以写:

bool isEven = a % 2 == 0;

bool isEven = (a & 1) == 0;

为什么使用 条件运算符 if语句?

一般来说,当你有类似的东西时:

if (condition)
{
    x = true;
}
else
{
    x = false;
}

x = condition ? true : false;

你可以写:

x = condition;

我认为C#编译器无论如何都会对此进行优化,我不会感到惊讶,但我不会像可读性那样关注性能。

答案 1 :(得分:3)

根据此benchmark comparisonif..else块的运行速度与?..:三元运行一样快,与单级切换/案例语句一样快。

因此,在我看来,为了获得更好的可读性和更清晰的陈述而不是担心性能会更好,因为它对你的情况影响很小。

答案 2 :(得分:0)

编辑:如果没有其他内容,请阅读底部的结论。休息试图向一般读者说明为什么结论有意义。

请注意,我对这个问题的直接反应是,这不重要。而且,对于大多数实际目的,你不应该发现任何差异......任何人甚至可能会争辩说,他们只是两种不同的方式来实现相同的事情 ...虽然可读性当然可能是不同,取决于编写这些语句所涉及的实际三个表达式。即。

  1. 条件的表达
  2. 如果条件为true
  3. ,则表达式
  4. 如果条件为false
  5. ,则表达式

    最重要的是,正如其他人所建议的那样,你应该能够通过(我假设发布版本)来解决这个问题。

    1. 查看生成的IL。
    2. 使用大量迭代对代码进行定时。
    3. 但是假设你因为好或坏的原因而对微观优化感兴趣,我也很好奇并且自己看了看......纯粹是为了我的娱乐,这就是我找到的 -

      注意:以下结果必须视为绝对,因为

      1. 他们可能依赖于我不知道的几个因素,包括上面提到的我选择参加测试的3个表达。
      2. 即使已知所有因素,任何人都会争辩说,其中许多都是 C#编译器 JIT编译器的内部细节 ,因此可以随任何版本的编译器而改变。我的小好奇实验在我的机器上...... .Net 4.5Release构建。

      3. 实验1

        <强>代码

        int a = 1;
        int b = 2;
        int h = 0;
        
        h = a == b ? 10 : -10;
        GC.KeepAlive(h);
        
        h = a == b ? 10 : -10;

        IL

        IL_0006:  ldloc.0
        IL_0007:  ldloc.1
        IL_0008:  beq.s      IL_000e
        IL_000a:  ldc.i4.s   -10
        IL_000c:  br.s       IL_0010
        IL_000e:  ldc.i4.s   10
        IL_0010:  stloc.2
        
        h = a == b ? 10 : -10;

        汇编

        00482B61  mov         eax,dword ptr [ebp-8]  
        00482B64  cmp         eax,dword ptr [ebp-0Ch]  
        00482B67  je          00482B73  
        00482B69  nop  
        00482B6A  mov         dword ptr [ebp-14h],0FFFFFFF6h  
        00482B71  jmp         00482B7A  
        00482B73  mov         dword ptr [ebp-14h],0Ah  
        00482B7A  mov         eax,dword ptr [ebp-14h]  <-- Extra compared to 2nd experiment
        00482B7D  mov         dword ptr [ebp-10h],eax  <-- Extra compared to 2nd experiment
        

        实验2

        <强>代码

        int a = 1;
        int b = 2;
        int h = 0;
        
        if (a == b) h = 10; else h = -10;
        GC.KeepAlive(h);
        
        if (a == b) h = 10; else h = -10;

        IL

        IL_0006:  ldloc.0
        IL_0007:  ldloc.1
        IL_0008:  bne.un.s   IL_000f
        IL_000a:  ldc.i4.s   10
        IL_000c:  stloc.2                              <-- Extra compared to 1st experiment
        IL_000d:  br.s       IL_0012
        IL_000f:  ldc.i4.s   -10
        IL_0011:  stloc.2
        
        if (a == b) h = 10; else h = -10;

        汇编

        0086288E  mov         eax,dword ptr [ebp-8]  
        00862891  cmp         eax,dword ptr [ebp-0Ch]  
        00862894  jne         008628A0  
        00862896  mov         dword ptr [ebp-10h],0Ah  
        0086289D  nop  
        0086289E  jmp         008628A7  
        008628A0  mov         dword ptr [ebp-10h],0FFFFFFF6h  
        

        分析

        我再说一遍,不是来自C#或JIT编译器团队,这个分析只是我的猜测(我可能犯了错误,错误的假设),而且很可能是实现细节< / strong>,将来可能会发生变化。最后,但并非最不重要的是,优化可能很好地受到其他几个因素/变化 的影响。

        基于IL:

        实验1 生成的IL看起来更好,从某种意义上说,虽然最终结果显然是相同的,但编译器确实已经生成了1条额外的IL指令实验2 。即if块和else块的每个表达式,似乎都是独立处理的,因此,当您和我知道代码涉及赋值变量h时,编译器会处理这两个代码块是单独的,如果使用h,则生成用于分配给if..else..的独立代码。

        IL_000c:  stloc.2                              <-- Extra compared to 1st experiment
        ....
        IL_0011:  stloc.2
        

        因此,使用?:运算符,似乎会生成较小的 IL ,因此可能会有更好的代码..(小代码可以也更快,因为那里在CPU的执行缓存中加载的指令较少,因此根据其他条件执行速度较快的机会很小。但是,请注意这是机器代码。)

        稍后,实验1 应该优于实验2 ,被证明 不正确 ,基于由 JIT编译器生成的汇编代码。 (只是为了证明,虽然IL优化有其地位..最后,重要的是由 JIT编译器生成的代码)。

        基于程序集(计数器直观):

        事实证明,当橡胶碰到道路时,即装配时,实验1 的指令比实验2 两个,并且应该运行得更慢(在微观层面)。这是我的猜测为什么 -

        IL_0008:  beq.s      IL_000e
        IL_000a:  ldc.i4.s   -10
        IL_000c:  br.s       IL_0010
        IL_000e:  ldc.i4.s   10
        IL_0010:  stloc.2
        
        
        00482B6A  mov         dword ptr [ebp-14h],0FFFFFFF6h   <-- IL_000a:  ldc.i4.s   -10
        
        00482B73  mov         dword ptr [ebp-14h],0Ah          <-- IL_000e:  ldc.i4.s   10
        00482B7A  mov         eax,dword ptr [ebp-14h]          <-- IL_0010:  stloc.2
        00482B7D  mov         dword ptr [ebp-10h],eax          <-- IL_0010:  stloc.2
        

        dword ptr [ebp-14h]显然是一个临时变量,它存储表达式#2 (条件为true)或上面的表达式#3 (条件是false)..(行为可能完全根据表达式本身而变化..我不知道)..然后必须移动值从临时变量到CPU寄存器的值,然后从CPU寄存器到堆栈上为局部变量h分配的位置。

        IL_0008:  bne.un.s   IL_000f
        IL_000a:  ldc.i4.s   10
        IL_000c:  stloc.2
        IL_000d:  br.s       IL_0012
        IL_000f:  ldc.i4.s   -10
        IL_0011:  stloc.2
        
        00862896  mov         dword ptr [ebp-10h],0Ah          <-- IL_000a:  ldc.i4.s   10
                                                               <-- IL_000c:  stloc.2
        
        008628A0  mov         dword ptr [ebp-10h],0FFFFFFF6h   <-- IL_000f:  ldc.i4.s   -10
                                                               <-- IL_0011:  stloc.2
        

        这里很清楚,值10需要加载到堆栈上的潜在临时变量,然后立即从该位置移动到位置堆栈上的变量h 。我想,通过将两个加载商店说明紧挨着彼此,对于 JITter来说应该很容易认为它可以直接将值存储到局部变量中,没有需要临时变量。因此, 看似 未优化的IL代码应该在生成汇编代码 JITter 时表现更好。


        结论

        虽然我个人的选择是在晚上花时间写下这个长篇答案,但感谢所有读完这篇文章以达到我最终结论的人。

        1. 微优化不仅可以通过更高的语言级别构造而且可以因各种其他因素(可能不在您的控制中)而变化。我猜测,3个表达式中的代码将 as非常重要您选择if..else.. vs ?:,以及所使用的编译器和运行时版本,以及操作系统上的其他条件。

        2. 因此,假设微优化对您来说非常重要,除非您已准备好运行性能测试,针对您的特定代码(忽略另一个答案中提供的基准测试。在许多情况下,运行自己的测试进行此类微优化本身就很难,因为它们很容易受到外部系统因素影响,如OS Scheduler等),您在此代码中每次更改/每次运行时版本更改(要检查效果回归),您遇到的问题毫无意义,而且您的问题有绝对答案。

          < / LI>

          PS - 我希望,这个答案成为Stack Overflow上这类(通用微优化/性能)问题的一个很好的例子,几乎每天都会遇到这样的问题:(。