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 comparison,if..else
块将 运行 与 ?..:
三元运算一样快与单级 switch/case 语句一样快.
因此,在我看来,最好是寻求更好的可读性和更清晰的陈述,而不是担心性能,因为它对您的情况影响最小。
编辑:如果不出意外,请阅读底部的结论。 Rest 试图向一般人说明 reader 为什么结论有意义。
请注意,我对这个问题的第一反应是,没关系。而且,出于大多数实际目的,你不应该发现任何区别......任何人甚至可能会争辩说,它们只是实现相同事物的两种不同方式......尽管可读性可能当然会有所不同,具体取决于编写这些语句所涉及的实际三个表达式。即
- 条件表达式
- 表达式如果条件是
true
- 表达式如果条件是
false
最重要的是,正如其他人所建议的那样,您应该能够通过(我假设是发布版本)来解决这个问题
- 查看生成的 IL。
- 用大量迭代对代码进行计时。
但是假设您出于好或坏的原因对微优化感兴趣,我也很好奇并亲自查看了它..纯属娱乐,这就是我发现的 -
注意:下面的发现必须而不是绝对的,因为
- 它们可能取决于几个我不知道的因素,包括上面提到的 3 个表达式,我选择放在我的测试中。
- 即使知道所有因素,任何人都会争辩说,其中许多是 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
在堆栈 上的位置。我想,通过让两个 load 和 store 指令 紧挨着彼此 ,它应该是JITter 可以很容易地认为它可以直接将值存储到局部变量中,不需要 临时变量 变量。因此,看似 未优化的 IL 代码应该在生成的汇编代码 JITter 下执行得更好。
结论
虽然我个人选择在晚上花时间写这个长答案,但感谢所有通读本文并得出我的最终结论的人。
微优化不仅会因更高的语言级别构造而异,还会因各种其他因素(可能不在您的控制范围内)而异。我猜,3 个表达式中的代码将是与你选择if..else..
还是?:
一样重要,还有编译器的版本和运行使用的时间,以及其他条件在 OS.
因此,假设微优化对您来说非常重要,除非您准备好运行您的性能测试,针对您非常具体的代码 (忽略另一个答案中提供的基准......在许多情况下,运行 自己对此类微优化进行测试本身就非常困难,因为它们很容易受到外部系统范围因素的影响,例如 OS 调度程序等),您在此代码中所做的每次更改/每次 运行时间版本更改(检查性能 回归 ), 你的问题毫无意义,你的问题没有绝对答案
PS - 我希望这个答案成为 Stack Overflow 上这类(通用微优化/性能)问题的一个很好的例子,几乎有人遇到过每天:(.
哪个在 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 comparison,if..else
块将 运行 与 ?..:
三元运算一样快与单级 switch/case 语句一样快.
因此,在我看来,最好是寻求更好的可读性和更清晰的陈述,而不是担心性能,因为它对您的情况影响最小。
编辑:如果不出意外,请阅读底部的结论。 Rest 试图向一般人说明 reader 为什么结论有意义。
请注意,我对这个问题的第一反应是,没关系。而且,出于大多数实际目的,你不应该发现任何区别......任何人甚至可能会争辩说,它们只是实现相同事物的两种不同方式......尽管可读性可能当然会有所不同,具体取决于编写这些语句所涉及的实际三个表达式。即
- 条件表达式
- 表达式如果条件是
true
- 表达式如果条件是
false
最重要的是,正如其他人所建议的那样,您应该能够通过(我假设是发布版本)来解决这个问题
- 查看生成的 IL。
- 用大量迭代对代码进行计时。
但是假设您出于好或坏的原因对微优化感兴趣,我也很好奇并亲自查看了它..纯属娱乐,这就是我发现的 -
注意:下面的发现必须而不是绝对的,因为
- 它们可能取决于几个我不知道的因素,包括上面提到的 3 个表达式,我选择放在我的测试中。
- 即使知道所有因素,任何人都会争辩说,其中许多是 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
在堆栈 上的位置。我想,通过让两个 load 和 store 指令 紧挨着彼此 ,它应该是JITter 可以很容易地认为它可以直接将值存储到局部变量中,不需要 临时变量 变量。因此,看似 未优化的 IL 代码应该在生成的汇编代码 JITter 下执行得更好。
结论
虽然我个人选择在晚上花时间写这个长答案,但感谢所有通读本文并得出我的最终结论的人。
微优化不仅会因更高的语言级别构造而异,还会因各种其他因素(可能不在您的控制范围内)而异。我猜,3 个表达式中的代码将是与你选择
if..else..
还是?:
一样重要,还有编译器的版本和运行使用的时间,以及其他条件在 OS.因此,假设微优化对您来说非常重要,除非您准备好运行您的性能测试,针对您非常具体的代码 (忽略另一个答案中提供的基准......在许多情况下,运行 自己对此类微优化进行测试本身就非常困难,因为它们很容易受到外部系统范围因素的影响,例如 OS 调度程序等),您在此代码中所做的每次更改/每次 运行时间版本更改(检查性能 回归 ), 你的问题毫无意义,你的问题没有绝对答案
PS - 我希望这个答案成为 Stack Overflow 上这类(通用微优化/性能)问题的一个很好的例子,几乎有人遇到过每天:(.