基于布尔值的 C 评估 - 哪个更快,哪个风格更好?
C evaluation based on boolean - which is faster, which is better style?
似乎在一般情况下,在 C(和许多其他语言)中,人们同样可以做到这一点,例如:
if (x > 0){
y = value1;
}
else{
y = value2;
}
或
y = (x>0)*value1 + (x <= 0)*value2;
第一种情况在文体上似乎更好,因为它更易于阅读(对大多数人而言?)。但话又说回来,第二种情况更紧凑。更重要的是,性能有什么不同吗?似乎第二种情况可能会更慢,因为两个布尔表达式都被评估了,并且乘以零..但是,我依稀记得 if 语句有一些小的额外开销?我意识到我实际上可以测量速度,但希望有人能给出更笼统的答案。
y = (x>0)*value1 + (x <= 0)*value2;
这是糟糕的代码,不应该被编写(您需要 100% 确定比较运算符 return 1
for "true";是否案例受制于很多东西)。
这就是类 C 语言中的 三元运算符 的用途:
y = (x > 0) ? value1 : value2;
More importantly, is there any difference in performance?
我很想就 "importance" 进行辩论,但是:
没有。如果编译器值得的话,三元运算符应该扩展为与 if
构造相同的字节码。你的 y=(cond)*a+(!cond)*b
构造更有可能变慢,因为奇怪的乘法滥用;但话又说回来,现代的优化编译器可能会杀死它,无论如何。
使用 if
所涉及的性能损失是由于它涉及分支这一事实。但是,像 (x > 0) 这样的表达式很可能也涉及分支。此外,正如您所提到的,在您的单个表达式中,您正在评估两个不同的条件,而在 if
中您只评估一个。正如其他人所提到的,编译器可能会将单个表达式和 if
优化为相同的代码。最终,if
要好得多,因为它很清楚它在做什么。正如@Ed 提到的,如果您想优化性能,请进行分析。然后您可以专注于花费最多时间的代码部分。
首先是正确性代码,然后是清晰性代码(当然,这两者通常是相关联的!)。最后,只有当你有实际需要的真实经验证据时,你才能考虑优化。过早的优化真的是邪恶的。优化几乎总是会花费您时间、清晰度和可维护性。你最好确定你买的是物有所值的东西。
y = (x>0)*value1 + (x <= 0)*value2;
不要在您的任何代码中使用它。这是关于如何编写 terrible
代码的一个很好的例子,因为它根本不直观。此外,您是否会获得任何性能提升取决于您的机器架构(取决于您的架构 multiplication instruction
所采用的周期数)。
但是,C 和 C++ 中的条件语句(例如 if else
)在最低级别(在硬件中)是昂贵的。要了解原因,您必须了解 pipelines 的工作原理。它会导致管道刷新并降低处理器的效率。
Linux kernel
对条件语句使用优化技术,__builtin_expect
。在使用条件语句(if-else 语句)时,我们通常知道哪个分支为真,哪个分支不为真(最有可能)。如果编译器提前知道这些信息,它就可以生成最优化的代码。
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
if (likely(x > 0)) {
y = value1;
} else {
y = value2;
}
对于上面的例子,我已经将if
条件标记为likely()
真,所以编译器会将真代码紧跟在分支之后,而假代码放在分支指令中。这样编译器就可以实现优化。但不要盲目使用 likely()
和 unlikely()
宏。如果预测正确,则意味着跳转指令的周期为零,但如果预测错误,则需要几个周期,因为处理器需要刷新其流水线,这比没有预测更糟糕。
似乎在一般情况下,在 C(和许多其他语言)中,人们同样可以做到这一点,例如:
if (x > 0){
y = value1;
}
else{
y = value2;
}
或
y = (x>0)*value1 + (x <= 0)*value2;
第一种情况在文体上似乎更好,因为它更易于阅读(对大多数人而言?)。但话又说回来,第二种情况更紧凑。更重要的是,性能有什么不同吗?似乎第二种情况可能会更慢,因为两个布尔表达式都被评估了,并且乘以零..但是,我依稀记得 if 语句有一些小的额外开销?我意识到我实际上可以测量速度,但希望有人能给出更笼统的答案。
y = (x>0)*value1 + (x <= 0)*value2;
这是糟糕的代码,不应该被编写(您需要 100% 确定比较运算符 return 1
for "true";是否案例受制于很多东西)。
这就是类 C 语言中的 三元运算符 的用途:
y = (x > 0) ? value1 : value2;
More importantly, is there any difference in performance?
我很想就 "importance" 进行辩论,但是:
没有。如果编译器值得的话,三元运算符应该扩展为与 if
构造相同的字节码。你的 y=(cond)*a+(!cond)*b
构造更有可能变慢,因为奇怪的乘法滥用;但话又说回来,现代的优化编译器可能会杀死它,无论如何。
使用 if
所涉及的性能损失是由于它涉及分支这一事实。但是,像 (x > 0) 这样的表达式很可能也涉及分支。此外,正如您所提到的,在您的单个表达式中,您正在评估两个不同的条件,而在 if
中您只评估一个。正如其他人所提到的,编译器可能会将单个表达式和 if
优化为相同的代码。最终,if
要好得多,因为它很清楚它在做什么。正如@Ed 提到的,如果您想优化性能,请进行分析。然后您可以专注于花费最多时间的代码部分。
首先是正确性代码,然后是清晰性代码(当然,这两者通常是相关联的!)。最后,只有当你有实际需要的真实经验证据时,你才能考虑优化。过早的优化真的是邪恶的。优化几乎总是会花费您时间、清晰度和可维护性。你最好确定你买的是物有所值的东西。
y = (x>0)*value1 + (x <= 0)*value2;
不要在您的任何代码中使用它。这是关于如何编写 terrible
代码的一个很好的例子,因为它根本不直观。此外,您是否会获得任何性能提升取决于您的机器架构(取决于您的架构 multiplication instruction
所采用的周期数)。
但是,C 和 C++ 中的条件语句(例如 if else
)在最低级别(在硬件中)是昂贵的。要了解原因,您必须了解 pipelines 的工作原理。它会导致管道刷新并降低处理器的效率。
Linux kernel
对条件语句使用优化技术,__builtin_expect
。在使用条件语句(if-else 语句)时,我们通常知道哪个分支为真,哪个分支不为真(最有可能)。如果编译器提前知道这些信息,它就可以生成最优化的代码。
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
if (likely(x > 0)) {
y = value1;
} else {
y = value2;
}
对于上面的例子,我已经将if
条件标记为likely()
真,所以编译器会将真代码紧跟在分支之后,而假代码放在分支指令中。这样编译器就可以实现优化。但不要盲目使用 likely()
和 unlikely()
宏。如果预测正确,则意味着跳转指令的周期为零,但如果预测错误,则需要几个周期,因为处理器需要刷新其流水线,这比没有预测更糟糕。