为什么编译器不优化集合元素上的空范围循环?
Why doesn't the compiler optimize an empty ranged-for loop over the elements of a set?
在测试我的代码时,我注意到当空的范围for loop
被删除与否时,执行时间显着增加。 通常,我会认为编译器会注意到 for 循环没有任何用途,因此会被忽略。作为编译器标志,我正在使用-O3
(gcc 5.4
(。我还用向量而不是集合对其进行了测试,这似乎有效,并且在两种情况下都提供了相同的执行时间。似乎迭代器的增量会花费所有额外的时间。
第一种情况,远程 for 循环仍然存在(慢(:
#include <iostream>
#include <set>
int main () {
long result;
std::set<double> results;
for (int i = 2; i <= 10000; ++i) {
results.insert(i);
for (auto element : results) {
// no operation
}
}
std::cout << "Result: " << result << "n";
}
删除范围 for 循环的第二种情况(快速(:
#include <iostream>
#include <set>
int main () {
long result;
std::set<double> results;
for (int i = 2; i <= 10000; ++i) {
results.insert(i);
}
std::cout << "Result: " << result << "n";
}
在内部std::set
迭代器使用某种指针链。这似乎是问题所在。
以下是与您的问题类似的最小设置:
struct S
{
S* next;
};
void f (S* s) {
while (s)
s = s->next;
}
这不是复杂的集合实现或迭代器开销的问题,而只是优化器无法优化的指针链模式。
不过,我不知道优化器在这种模式下失败的确切原因。
另外,请注意,此变体已优化:
void f (S* s) {
// Copy paste as many times as you wish the following two lines
if(s)
s = s->next;
}
编辑
正如@hvd所建议的,这可能与编译器无法证明循环不是无限的有关。如果我们像这样编写 OP 循环:
void f(std::set<double>& s)
{
auto it = s.begin();
for (size_t i = 0; i < s.size() && it != s.end(); ++i, ++it)
{
// Do nothing
}
}
编译器优化了所有内容。
基于循环的范围并不像看起来那么简单。它在编译器内部转换为基于迭代器的循环,如果迭代器足够复杂,则标准甚至可能不允许编译器删除这些迭代器操作。
您可以使用 clang 优化报告。在启用save-optimization-record
的情况下编译代码,因此优化报告将被转储到 main.opt.yaml
。
clang++ -std=c++11 main.cpp -O2 -fsave-optimization-record
您将看到循环存在几个问题:
Clang认为,在这个循环中修改了一个值。
- String: value that could not be identified as reduction is used outside the loop
此外,编译器无法计算循环迭代的次数。
- String: could not determine number of loop iterations
请注意,编译器成功地内联了begin
、end
、operator++
和operator=
。
Range-for 是"语法糖",这意味着它所做的只是为可以用更详细的方式表达的东西提供速记符号。例如,range-for 转换为类似的东西。
for (Type obj : container)
->
auto endpos = container.end();
for ( auto iter=container.begin(); iter != endpos; ++iter)
{
Type obj(*iter);
// your code here
}
现在的问题是开始/结束/*iter/++iter/(obj = (是函数调用。为了优化它们,编译器需要知道它们没有副作用(对全局状态的更改(。编译器是否可以执行此操作是实现定义的,并且取决于容器类型。不过我能说的是,在大多数情况下,您不需要(obj =(函数,因此更喜欢
for (const auto& X: cont)
或。。。
for (auto& X: cont)
自。。。
for (auto X : cont)
您可能会发现这足以简化它,以便启动优化。
- 基于范围循环到旧编译器的旧样式
- 带有自定义对象的C 范围循环
- 为什么编译器不优化集合元素上的空范围循环?
- 迭代在包含向量的vor in for for范围循环中迭代
- 我们可以在范围循环中使用引用(而不是指针)上的删除
- 如何实现我的自定义范围循环
- C++范围循环
- GCC 4.4 不实现 C++11 范围循环.它还支持哪些其他范围循环语法
- 嵌套c++11范围循环,用于查找组合
- 范围循环中的访问索引
- 对于不可复制类型的范围循环,是否可能
- 我的范围循环出现逻辑错误
- 如何在我的类上允许范围循环
- c++的范围循环比递减循环快
- 通过范围循环从指针容器中获取取消引用元素的引用
- c++ 11:为什么这个范围循环会使FPS下降35
- 带有自定义类的范围循环(矢量的简单版本<string>,使用分配器)
- MSVC10 Visual Studio 2010支持c++范围循环吗?
- 围绕 2 的幂范围循环变量
- toupper() 在 for 范围循环中不起作用