if-else 和 哪个更快? :

Which is faster if-else or ? :

哪个在 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;

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

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

你的第二个代码甚至无法编译。但你可以只写:

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# 编译器对此进行优化我不会感到惊讶,但我不会像关注可读性那样关注性能。

根据此 benchmark comparisonif..else 块将 运行 与 ?..: 三元运算一样快与单级 switch/case 语句一样快.

因此,在我看来,最好是寻求更好的可读性和更清晰的陈述,而不是担心性能,因为它对您的情况影响最小。

编辑:如果不出意外,请阅读底部的结论。 Rest 试图向一般人说明 reader 为什么结论有意义。

请注意,我对这个问题的第一反应是,没关系。而且,出于大多数实际目的,你不应该发现任何区别......任何人甚至可能会争辩说,它们只是实现相同事物的两种不同方式......尽管可读性可能当然会有所不同,具体取决于编写这些语句所涉及的实际三个表达式。即

  1. 条件表达式
  2. 表达式如果条件是true
  3. 表达式如果条件是false

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

  1. 查看生成的 IL。
  2. 用大量迭代对代码进行计时。

但是假设您出于好或坏的原因对微优化感兴趣,我也很好奇并亲自查看了它..纯属娱乐,这就是我发现的 -

注意:下面的发现必须而不是绝对的,因为

  1. 它们可能取决于几个我不知道的因素,包括上面提到的 3 个表达式,我选择放在我的测试中。
  2. 即使知道所有因素,任何人都会争辩说,其中许多是 C# 编译器JIT 编译器,因此可以随任一编译器的任何版本而变化。我的小好奇实验是在我的机器上.. .Net 4.5, Release build.

实验 1

代码

int a = 1;
int b = 2;
int h = 0;

h = a == b ? 10 : -10;
GC.KeepAlive(h);

IL 对于 h = a == b ? 10 : -10;

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);

IL 对于 if (a == b) h = 10; else h = -10;

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 编译器团队,此分析只是我的猜测(我可能犯了错误,错误的假设),而且很可能是 实现细节,可能会在未来的版本中改变。最后但并非最不重要的一点是,优化很可能是由其他几个因素/变化驱动的这里根本没有考虑。

基于 IL:

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

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

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

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

基于汇编(反直觉):

事实证明,当橡胶撞到路上时,即在装配中,实验 1 比 [=] 多 两个 指令107=]实验 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] 显然是一个 临时 变量,它存储 Expression #2 的结果(条件是 true),或上面的 Expression #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 在堆栈 上的位置。我想,通过让两个 loadstore 指令 紧挨着彼此 ,它应该是JITter 可以很容易地认为它可以直接将值存储到局部变量中,不需要 临时变量 变量。因此,看似 未优化的 IL 代码应该在生成的汇编代码 JITter 下执行得更好。


结论

虽然我个人选择在晚上花时间写这个长答案,但感谢所有通读本文并得出我的最终结论的人。

  1. 微优化不仅会因更高的语言级别构造而异,还会因各种其他因素(可能不在您的控制范围内)而异。我猜,3 个表达式中的代码将是你选择if..else..还是?:一样重要,还有编译器的版本和运行使用的时间,以及其他条件在 OS.

  2. 因此,假设微优化对您来说非常重要,除非您准备好运行您的性能测试,针对您非常具体的代码 (忽略另一个答案中提供的基准......在许多情况下,运行 自己对此类微优化进行测试本身就非常困难,因为它们很容易受到外部系统范围因素的影响,例如 OS 调度程序等),您在此代码中所做的每次更改/每次 运行时间版本更改(检查性能 回归 ), 你的问题毫无意义,你的问题没有绝对答案

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