2

在我的 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];
    }
}

我得到 7017 aa, 6739 bb, 1130 cc

为什么 for range 循环很慢,对性能的影响如此之大?你怎么看待这件事?如果有人感兴趣 - 当我通过引用而不是通过值更改传递 str 时,结果几乎相同。

顺便提一句。我使用 MSVS 2012

4

2 回答 2

6

(从评论移动)

基于范围的循环使用迭代器,VC++ 迭代器在调试模式下往往由于额外的调试检查而变慢;一旦处于发布模式,它们应该归结为指针,因此应该没有性能差异。

此外,您的测试没有意义,因为:

  • 这样的空循环可能会被优化器优化掉,因此您只能在未优化的构建中可靠地运行测试 - 但在未优化的构建中它没有意义,因此测试性能;
  • 您只以特定顺序运行每个测试一次 - 这可能会提供误导性信息,例如由于第一个循环中的缓存“预热”(尽管我不认为是这种情况,因为str刚刚被访问构建); 要获得具有统计意义的信息,您必须以随机顺序多次重复测试。
于 2013-08-17T11:14:24.213 回答
4

我拿了你的代码并写了这个:

#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 总和=1048576000

增加优化以-O3产生更大的差异:

aa=190000 bb=170000 cc=450000 sum=1048576000

请注意,Linux 中的“时钟”不是很准确,因为它实际上是给应用程序使用的 CPU 时间,它只更新了 10 毫秒,所以我不得不使循环更大一点。

很明显,您的断言是错误的,因为在这两种情况下,第三种变体都是最慢的。我只能得出结论,您是在进行低优化或没有优化的基准测试。

于 2013-08-17T11:24:43.960 回答