问题上下文:[C ++]我想知道理论上最快的是什么,以及编译器会做什么。我不想听到过早优化是所有邪恶的根源等。
我正在写一些这样的代码:
bool b0 = ...;
bool b1 = ...;
if (b0 && b1)
{
...
}
但后来我在想:如果没有优化编译,代码将按原样编译成两个TEST指令。这意味着两个分支。所以我认为写作可能更好:
if (b0 & b1)
如果编译器没有进行优化,那么只生成一条TEST指令。但后来我觉得这违背了我的代码风格。我通常会写&&
和||
。
问:如果我打开优化标记(-O1
,-O2
,-O3
,-Os
和{{},编译器会怎么做? 1}})。即使我在代码中使用了-Ofast
,编译器也会像&
一样自动编译它吗?什么在理论上更快?如果我这样做,行为是否会改变:
&&
问:正如我猜想的那样,这非常依赖于这种情况,但这是编译器用if (b0 && b1)
{ ... }
else if (b0)
{ ... }
else if (b1)
{ ... }
else
{ ... }
替换&&
的常见技巧?
答案 0 :(得分:3)
如果你关心什么是最快的,为什么你关心编译器没有优化会做什么?
问:正如我猜想的那样,这非常依赖于这种情况,但这是编译器用
&&
替换&
的常见技巧?
这个问题似乎假设编译器将C ++代码转换为更多的C ++代码。它没有。它将您的代码转换为机器指令(包括汇编程序作为编译器的一部分,以供参数使用)。您不应该假设从&&
或&
等C ++运算符到特定指令的一对一映射。
通过优化,编译器可以做任何它认为会更快的事情。如果单个指令更快,编译器将为if (b0 && b1)
生成单个指令,您不需要使用微优化来编写代码来帮助它进行如此简单的转换。
编译器知道它使用的指令集,它知道条件所处的上下文,是否可以作为死代码完全删除,或者移动到其他地方以帮助管道,或者通过常量传播简化等等等等。
如果您真的关心什么是最快的,为什么在您知道实际需要之前计算b1
?如果获得b1
的值没有副作用,编译器甚至可以将您的代码转换为:
bool b0 = ...;
if (b0)
{
bool b1 = ...;
if (b1)
{
这是否意味着两个if
条件比&
更快?!当然不是。
换句话说,问题的整个前提是有缺陷的。在误导性的追求中,不要损害代码的可读性和简单性。理论上最快的"微优化。花时间改进使用的算法和数据结构不试图再次猜测编译器将生成哪些指令。
答案 1 :(得分:3)
问:如果我打开优化标志(-O1,-O2,-O3,-Os和-Ofast),编译器会做什么。
最有可能的是增加优化。 正如我的评论中所述,您实际上无法进一步优化评估:
AND B0 WITH B1 (sets condition flags)
JUMP ZERO TO ...
虽然,如果你有很多简单的布尔逻辑或数据操作,一些处理器可能会有条件地执行它们。
即使我使用了&&和编译器,编译器也会像&一样自动编译它。在代码中?
什么在理论上更快?
在大多数平台上,A & B
与A && B
的评估没有区别
在最终评估中,执行比较或AND指令,然后根据状态进行跳转。两条指令。
大多数处理器没有布尔寄存器。这都是数字和位。
您最好的选择是检查设计并设置算法以使用布尔代数。您可以简化布尔表达式。
另一种选择是实现代码,以便编译器可以生成条件汇编指令(如果平台支持它们)。
处理器倾向于通过跳转进行算术和数据传输。
许多处理器总是在提供指令管道。当涉及条件分支指令时,处理器必须等待(暂停指令预取)直到确定条件状态。然后它可以确定下一条指令的取出位置。
如果无法移除跳转(例如循环),请在数据端使数据处理的比率跳得更大。搜索“循环展开”。当优化级别增加时,许多编译器将执行此操作。
通过组织数据以获得最佳数据缓存,您可能会注意到性能的提升。
例如,使用包含3个元素的结构的一个数组,而不是3个大数组。这允许使用中的元素彼此靠近(并且降低了访问缓存之外的数据的可能性)。
A && B
与A & B
作为条件表达式的评估差异称为微优化。通过使用布尔代数减少条件表达式的数量,您将获得更高的性能。跳转或执行路径的更改会降低指令执行速度。在数据高速缓存外部获取数据也会降低执行速度。通过重新设计代码并帮助编译器减少分支并更有效地使用数据缓存,您很可能会获得更好的性能。