当前位置: 首页 > 知识库问答 >
问题:

编译器会针对循环优化它吗?

叶鹭洋
2023-03-14

在 C 或 C 中,如果编译器遇到一个 for 循环,其中计数器从 0 计数到 n, n 是一个变量(不是函数调用,也不是常量),编译器是否会通过检查变量 n(绑定变量)是否会在循环期间更改(访问写入, 例如:n 可以是循环前计算的字符串的长度),通过优化这里,我的意思是将其值复制到寄存器以避免内存访问?

下面是一个示例:

for (int c = 0; c < n; c++) {
    // do something not related to n
}

编译器会注意到这一点并对其进行优化吗?

共有3个答案

朱锐
2023-03-14

您应该尝试自己编译和查看。编译器中的优化取决于几件事。

无论如何,为了回答你的问题,我唯一能做的就是给你提供一个最相似的具体案例。

代码很简单:

#include <string>

int main(int argc, char *argv[]) {
  std::string str = "this_is_a_string";
  int size = str.size();
  for (int i = 0; i < size; ++i) {
    str += std::to_string(i);
  }
  return 0;
}

结果汇编代码是(针对不同的优化级别):

 movl   $0x0,-0x14(%rbp)    // int i = 0;
 mov    -0x14(%rbp),%eax    // load i into the register
 cmp    -0x18(%rbp),%eax    // load size and compare with i in the register
 jge    401317 <main+0x91>  // jump if >=
 // initialization up
 add    $0x1,%ebx         // ++i (now i is stored in register)
 cmp    %ebx,-0x5c(%ebp)  // compare i and size (which is load from memory)
 je     0x80488a3 <main(int, char**)+136>  // jump if = (and not >=)

相同的-O1

这里是用过的代码,带有汇编

程磊
2023-03-14

结果取决于使用的编译器。

编译器可以使用n的处理器寄存器,您仍然可以在循环中修改n。所以无论如何最小的优化是可能的。

如果您有一个指向n的指针,并且您使用指针间接更改n的值,则将变量的值放置在处理器寄存器中可能会导致“混淆现象”问题。

例如:

int n = 4;
int *nptr = &n;
for(int i = 0; i < n; ++i)
  --*nptr;

编译器必须知道nptr是n的别名,因此每次访问都必须从内存中读取n,但在许多情况下,编译器根本没有机会知道n和nptr之间的关系。

您可以使用 volatile 关键字来阻止编译器优化变量(即 volatile int n = 4;

单于翰飞
2023-03-14

c/c编译器会通过检查变量n(绑定变量)是否会在循环过程中改变来优化循环吗

“as-if”规则允许编译器重新排序或重新编写代码,只要可观察的效果是“好像”它执行了您编写的代码。

这里的关键词是“可观察”。除非可以证明n在循环体的执行过程中不能改变,否则循环必须重新计算c

如果编译器可以访问循环中的所有代码(例如,它是在同一个编译单元中定义的,或者全局优化在链接期间有另一个外观,< code>n永远不会被引用或指针别名化),那么它很可能能够证明< code>n没有改变。

在这种情况下,你不应该惊讶地看到循环以某种方式进行了优化。

 类似资料:
  • 我在godbolt上编译这段代码。orgwith-O2和编译器不会使用一些memcpy对其进行优化,而是诚实地运行循环。 但是,如果我将“= src[i]”替换为“= 0”,他们会使用memset。但同样,当我用“ = 1”替换它时,它们会运行一个循环。当要设置的值不为零时,为什么它们会避免使用 memcpy 和 memset?我认为这是他们将执行的第一批优化之一。

  • 问题内容: 假设我在C代码中有类似的内容。我知道您可以使用a 代替,以使编译器不对其进行编译,但是出于好奇,我问编译器是否也可以解决此问题。 我认为这对于Java编译器来说更为重要,因为它不支持。 问题答案: 在Java中,if内的代码甚至都不是已编译代码的一部分。它必须编译,但不会写入已编译的字节码。它实际上取决于编译器,但我不知道没有对它进行优化的编译器。规则在JLS中定义: 优化的编译器可能

  • 我经常遇到这种情况。乍一看,我认为,“这是糟糕的编码;我正在执行一个方法两次,必然会得到相同的结果。”但想到这里,我不得不怀疑编译器是否像我一样聪明,并能得出相同的结论。 编译器的行为是否取决于 方法的内容?假设它看起来像这样(有点类似于我现在的真实代码): 除非对这些对象来自的任何存储进行处理不当的异步更改,否则如果连续运行两次,肯定会返回相同的内容。但是,如果它看起来像这样(为了论证而无意义的

  • 如果关闭了编译器优化(gcc-o0...),那么说'volatile'关键字没有区别是可以的吗? 我制作了一些示例“C”程序,并且仅当打开编译器优化时,才在生成的汇编代码中看到易失性和非易失性之间的区别,即((gcc-o1....)。

  • 根据这个问题,浮点除法与浮点乘法。由于某些原因,除法比乘法慢。 如果可能的话,编译器通常会用乘法代替除法吗? 例如: 会是: 如果它被认为是编译器可靠的问题,我使用的是VS2013默认编译器。但是,如果我得到一个通用的答案就好了(这种优化的理论有效性)

  • 问题内容: Java编译器会优化简单的重复数学运算,例如: 我知道我可以将结果分配给if语句之前的变量,然后返回变量,但这有点麻烦。如果编译器自动识别出正在执行相同的计算,并将结果自己缓存到临时变量中,我宁愿遵守上述约定。 *编辑-我是个白痴。我试图简单/过多地提出我的问题。它不是简单的:if(x> y) 问题答案: 答案是肯定的。这称为“ 公共子表达式消除”,它是Java,C / C ++和其他