在 C 或 C 中,如果编译器遇到一个 for
循环,其中计数器从 0
计数到 n,而
n 是一个变量(不是函数调用,也不是常量),编译器是否会通过检查变量 n
(绑定变量)是否会在循环期间更改(访问写入, 例如:
n
可以是循环前计算的字符串的长度),通过优化这里,我的意思是将其值复制到寄存器以避免内存访问?
下面是一个示例:
for (int c = 0; c < n; c++) {
// do something not related to n
}
编译器会注意到这一点并对其进行优化吗?
您应该尝试自己编译和查看。编译器中的优化取决于几件事。
无论如何,为了回答你的问题,我唯一能做的就是给你提供一个最相似的具体案例。
代码很简单:
#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
。
这里是用过的代码,带有汇编。
结果取决于使用的编译器。
编译器可以使用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;
)
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 ++和其他