构造长循环语句是一种好的做法吗

Is it good practice to construct long circuit statements?

本文关键字:一种 循环 语句      更新时间:2023-10-16

问题上下文:[C++]我想知道理论上什么是最快的,编译器会做什么。我不想听到过早优化是万恶之源等。

我在写这样的代码:

bool b0 = ...;
bool b1 = ...;
if (b0 && b1)
{
    ...
}

但后来我在想:如果在没有优化的情况下编译,代码将按原样编译成两个TEST指令。这意味着两个分支。所以我在想,最好写:

if (b0 & b1)

如果编译器没有进行优化,它将只生成一条TEST指令。但后来我觉得这违背了我的代码风格。我通常写&&||

Q:如果我打开优化标志(-O1-O2-O3-Os-Ofast),编译器会怎么做。即使我在代码中使用了&&,编译器会像&一样自动编译它吗?理论上什么更快?如果我这样做,行为会改变吗:

if (b0 && b1)
{ ... }
else if (b0) 
{ ... }
else if (b1)
{ ... }
else
{ ... }

Q:正如我所猜测的,这在很大程度上取决于情况,但编译器用&替换&&是一种常见的技巧吗?

Q: 如果我打开优化标志(-O1、-O2、-O3、-Os和-Ofast),编译器会怎么做。

很可能没有更多的东西来增加优化。正如我在评论中所说,你真的不能再优化评估了:

  AND B0 WITH B1 (sets condition flags)
  JUMP ZERO TO ...

尽管如此,如果您有很多简单的布尔逻辑或数据操作,一些处理器可能会有条件地执行它们。

编译器会自动编译它吗;,即使我使用了&代码中
理论上什么更快?

在大多数平台中,A & BA && B的评估没有差异
在最终评估中,执行比较或AND指令,然后根据状态进行跳转。两条说明。

大多数处理器没有布尔寄存器。都是数字和比特。

通过布尔逻辑优化

你最好的选择是审查设计,并设置你的算法使用布尔代数。您可以简化布尔表达式。

另一种选择是实现代码,以便编译器可以生成条件汇编指令(如果平台支持的话)。

优化:减少跳跃

处理器更喜欢算术和数据传输,而不是跳跃。

许多处理器总是向指令管道提供信息。当涉及到条件分支指令时,处理器必须等待(暂停指令预取),直到确定条件状态。然后它可以确定下一条指令将被提取到哪里。

如果不能消除跳跃,例如在循环中,则在数据端增大数据处理与跳跃的比率。搜索"循环展开"。当优化级别增加时,许多编译器将执行此操作。

优化:数据缓存

通过组织数据以获得最佳数据缓存使用率,您可能会注意到性能的提高。

例如,使用包含3个元素的结构的一个阵列,而不是3个大阵列。这允许使用中的元素彼此靠近(并降低访问缓存外数据的可能性)。

摘要

A && BA & B作为条件表达式的评估差异被称为微优化。通过使用布尔代数来减少条件表达式的数量,可以提高性能。跳转或更改执行路径会减慢指令执行速度。从数据缓存之外获取数据也会减慢执行速度。通过重新设计代码并帮助编译器减少分支和更有效地使用数据缓存,您很可能会获得更好的性能。

如果你关心什么是最快的,为什么你关心编译器在没有优化的情况下会做什么?

Q:正如我所猜测的,这在很大程度上取决于情况,但编译器用&替换&&是一种常见的技巧吗?

这个问题似乎假设编译器将C++代码转换为更多的C++代码。事实并非如此。它将代码转换为机器指令(为了参数起见,将汇编程序作为编译器的一部分)。您不应该假设存在从像&&&这样的C++运算符到特定指令的一对一映射。

通过优化,编译器将执行它认为更快的任何操作。如果一条指令会更快,编译器会为if (b0 && b1)生成一条指令,那么你不需要用微优化来帮助代码进行如此简单的转换。

编译器知道它正在使用的指令集,它知道条件所在的上下文,以及它是否可以作为死代码完全删除,或者移动到其他地方以帮助管道,或者通过不断传播来简化,等等。

如果你真的关心什么是最快的,为什么要计算b1,直到你知道它是真正需要的?如果获得b1的值没有副作用,编译器甚至可以将代码转换为:

bool b0 = ...;
if (b0)
{
  bool b1 = ...;
  if (b1)
  {

这是否意味着两个if条件比一个&条件快?!当然不是。

换句话说,这个问题的整个前提是有缺陷的。不要在错误地追求"理论上最快"的微优化的过程中损害代码的可读性和简单性。花时间改进所使用的算法和数据结构,而不是尝试猜测编译器将生成哪些指令。