应尽可能避免循环的范围
Should range for loop be avoided when possible?
在我的DirectX程序中,我写了一个循环,它通过std::string进行迭代,然后我运行了性能分析器,并意识到这个循环消耗了大量的cpu时间。更重要的是,当我运行我的程序时,我有大约1300 FPS。所以我决定对此做点什么,并将基于范围的循环更改为典型的迭代。我的意思是我变了:
for( char c : std_string_name )
至
for(size_t i=0; i<std_string_name.size(); ++i )
{ char c = std_string_name[i]; }
现在我有大约1900 FPS。这不是很奇怪吗?
接下来,我决定在没有任何循环体的情况下测试迭代器本身。下面我放了一个链接到我的测试结果的屏幕截图
str是一个长字符串,aa、bb、cc是时钟中的时间。
void main(){
for(int i=0;i<10;++i)
str += str;
clock_t a, b, c;
clock_t aa, bb, cc;
a=clock();
iter1(str);
aa = clock() -a ;
b=clock();
iter2(str);
bb=clock()-b;
c=clock();
iter3(str);
cc=clock()-c;
}
void iter1( std::string str ){
for( char c : str ){
}
}
void iter2( std::string str ){
for( char &c : str ){
}
}
void iter3( std::string str ){
for( size_t i=0;i<str.length();++i){
char c = str[i];
}
}
aa
得到7017,bb
得到6739,cc
得到1130。
为什么for range循环对性能的影响如此之大?你对此怎么看?如果有人感兴趣——当我改变了通过引用而不是通过值传递str时,结果几乎是一样的。
顺便说一句。我使用MSVS 2012
(从注释开始)
基于范围的循环使用迭代器,并且由于额外的调试检查,VC++迭代器在调试模式下往往很慢;一旦进入发布模式,它们就应该归结为指针,所以不应该有性能差异。
此外,您的测试没有意义,因为:
- 这样的空循环可能只是被优化器优化掉了,所以你只能在未优化的构建中可靠地运行测试——但在未优化构建中,这没有意义,所以测试性能
- 每个测试只运行一次,并且按特定顺序运行——这可能会提供误导性信息,例如,由于第一个循环中的缓存"预热"(尽管我不认为是这样,因为
str
刚刚被访问以构建);为了获得有统计意义的信息,你必须按随机顺序多次重复测试
我接受了您的代码并写下了以下内容:
#include <iostream>
#include <string>
#include <ctime>
int iter1( std::string str ){
int sum = 0;
for( char c : str ){
sum += c;
}
return sum;
}
int iter2( std::string str ){
int sum = 0;
for( char &c : str ){
sum += c;
}
return sum;
}
int iter3( std::string str ){
int sum = 0;
std::string::size_type len = str.length();
for( size_t i=0;i<len;++i){
char c = str[i];
sum += c;
}
return sum;
}
int main(){
std::string str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
for(int i=0;i<22;++i)
str += str;
clock_t a, b, c;
clock_t aa, bb, cc;
int sum = 0;
a=clock();
sum += iter1(str);
aa = clock() -a ;
b=clock();
sum += iter2(str);
bb=clock()-b;
c=clock();
sum += iter3(str);
cc=clock()-c;
std::cout << "aa=" << aa << " bb=" << bb << " cc=" << cc << " sum=" << sum << std::endl;
}
用g++ -O2 -Wall -std=c++0x iters.cpp
(gcc 4.6.3)编译得到以下结果:
aa=320000 bb=320000 cc=380000 sum=1048576000
将优化增加到-O3
会产生更大的差异:
aa=190000 bb=170000 cc=450000 sum=1048576000
请注意,Linux中的"时钟"不是很准确,因为它实际上给出了应用程序使用的CPU时间,而应用程序只更新了10ms,所以我不得不让循环变大一点。
很明显,你的断言是错误的,因为第三种变体在这两种情况下都是SLOWEST。我只能得出这样的结论:您正在进行低优化或无优化的基准测试。
- 基于范围循环到旧编译器的旧样式
- 带有自定义对象的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 范围循环中不起作用