9

C++ の豊富な抽象化には一定の計算オーバーヘッドが伴うことは常に知っていましたが、正しいコンパイラ最適化が適用されれば、このオーバーヘッドはほとんど無視できるという印象を受けました。このオーバーヘッドの正確な大きさに興味があったので、これを判断するための簡単なテストを作成しました。テストは、コンテナー変数を受け取り、コンテナー内の各要素に値を割り当ててから、別のループでコンテナー全体の値を合計する、テンプレート化された関数です。このプロセスは、あらかじめ設定されたサイクル数だけ繰り返されます。

非常に不安だったのは、ベクトルの実装が標準の配列の実装の 3 倍近くかかっていることでした。コンパイラーの最適化の膨大な選択肢を並べ替えた後、成功せずに、私は弾丸をかじり、アセンブリ コードを直接目で見て、時間のペナルティの原因を調べてみることにしました。いくつかのアセンブリ ディレクティブを含めて、配列のインデックス操作が発生した場所を正確に特定し、コードを詳細に調べました。私が完全に混乱したことに、ベクトル実装と配列実装の違いはまったく重要ではないことがわかりました。アセンブリ コードはここにあります。

これは、バイナリのビルドに使用したコマンドです。

g++ -O3 vectorArrayOp.cpp -o vectorArrayOp

これは、アセンブリをビルドするために使用したコマンドです。

g++ -O3 -DTAGASM vectorArrayOp.cpp -S -o vectorArrayOp.s

これは、バイナリを実行して観察した出力です。

gmurphy@interloper:Reference$ ./vectorArrayOp 
Duration 0.027678
Duration 0.090212

計算された値を stdout ストリームに含めても、結果は変更されません。明確にするために削除しました。私のシステム仕様は次のとおりです (AMD でも同じ結果が得られました)。

Linux 3.2.0-32-generic x86_64 GNU/Linux
Intel(R) Xeon(R) CPU X5550  @ 2.67GH
g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3

コードは次のとおりです。アセンブリが非常に似ているのに、なぜタイミングが非常に異なるのかについて、誰かが私に洞察を提供していただければ幸いです。

#include <vector>
#include <iostream>
#include <sys/time.h>
#ifdef TAGASM
#define ASMTAG(X) asm(X)
#else
#define ASMTAG(X)
#endif 
enum { DataSize=1024, NumTests=(1<<16) } ;
struct ReturnValue {ReturnValue(float _d, int _t):d(_d), t(_t){} float d; int t;} ;
template <typename Container, typename Type>
ReturnValue runTest(Container &c, Type value)
{
    int tagValue(0);
    timeval startTime;
    gettimeofday(&startTime, NULL);
    for(int i=0; i<NumTests; i++)
    {
        for(int j=0; j<DataSize; j++)
        {
            ASMTAG("preassign");
            c [j] = value ;
            ASMTAG("postassign");
        }
        for(int j=0; j<DataSize; j++)
        {
            ASMTAG("preadd");
            tagValue += c [j] ;
            ASMTAG("postadd");
        }
    }
    timeval endTime;
    gettimeofday(&endTime, NULL);
    float duration((endTime.tv_sec-startTime.tv_sec)+
                   (endTime.tv_usec-startTime.tv_usec)/1000000.0);
    //tagValue is returned in case the optimising compiler might try to remove the loops
    return ReturnValue(duration, tagValue) ;
}
int main()
{
    int *arrayData = new int [DataSize];
    std::vector <int> vectorData(DataSize, 0) ;
    ReturnValue ad = runTest(arrayData, 1);
    ReturnValue vd = runTest(vectorData, 1);
    std::cout<<"Duration "<<ad.d<<std::endl;
    std::cout<<"Duration "<<vd.d<<std::endl;
    delete [] arrayData;
    return 0 ;
}
4

1 に答える 1

11
% g++-4.4 -O3 vectorArrayOp.cpp -o vectorArrayOp
% ./vectorArrayOp
Duration 0.008581
Duration 0.008775
% g++-4.5 -O3 vectorArrayOp.cpp -o vectorArrayOp
% ./vectorArrayOp
Duration 0.008634
Duration 0.008588
% g++-4.6 -O3 vectorArrayOp.cpp -o vectorArrayOp
% ./vectorArrayOp
Duration 0.01731
Duration 0.081696
% g++-4.7 -O3 vectorArrayOp.cpp -o vectorArrayOp
% ./vectorArrayOp
Duration 0.008618
Duration 0.008612
% clang++ -O3 vectorArrayOp.cpp -o vectorArrayOp
% ./vectorArrayOp
Duration 0.066484
Duration 0.066435

これらの結果に基づくと、これはおそらくg++4.6でのコンパイラ固有のパフォーマンスの低下です。

于 2012-11-05T18:57:23.580 に答える