私はかなり長い間 C++11 コードを書いてきましたが、ベンチマークを行っていません。移動セマンティクスにより、ベクトル操作のようなものが「ただ速くなる」ことだけを期待しています。そのため、GCC 4.7.2 と clang 3.0 (Ubuntu 12.10 64 ビットのデフォルト コンパイラ) で実際にベンチマークを行うと、非常に満足のいく結果が得られません。これは私のテストコードです:
編集: @DeadMG と @ronag によって投稿された (良い) 回答に関して、要素の型を から を持たない に変更し、std::string
すべてmy::string
のswap()
内部文字列を大きく (200-700 バイト) して、そうならないようにしました。SSOの犠牲者。
EDIT2: COWが理由でした。偉大なコメントによってコードを再び適応させ、ストレージをstd::string
toから変更し、std::vector<char>
コピー/移動コンストラクターを除外しました (代わりにコンパイラーにそれらを生成させます)。COW がないと、実際には速度の差は非常に大きくなります。
EDIT3:でコンパイルしたときに以前のソリューションを再追加しました-DCOW
。これにより、内部ストレージが@chicostd::string
の要求に応じて ではなくになります。std::vector<char>
#include <string>
#include <vector>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <functional>
static std::size_t dec = 0;
namespace my { class string
{
public:
string( ) { }
#ifdef COW
string( const std::string& ref ) : str( ref ), val( dec % 2 ? - ++dec : ++dec ) {
#else
string( const std::string& ref ) : val( dec % 2 ? - ++dec : ++dec ) {
str.resize( ref.size( ) );
std::copy( ref.begin( ), ref.end( ), str.begin( ) );
#endif
}
bool operator<( const string& other ) const { return val < other.val; }
private:
#ifdef COW
std::string str;
#else
std::vector< char > str;
#endif
std::size_t val;
}; }
template< typename T >
void dup_vector( T& vec )
{
T v = vec;
for ( typename T::iterator i = v.begin( ); i != v.end( ); ++i )
#ifdef CPP11
vec.push_back( std::move( *i ) );
#else
vec.push_back( *i );
#endif
}
int main( )
{
std::ifstream file;
file.open( "/etc/passwd" );
std::vector< my::string > lines;
while ( ! file.eof( ) )
{
std::string s;
std::getline( file, s );
lines.push_back( s + s + s + s + s + s + s + s + s );
}
while ( lines.size( ) < ( 1000 * 1000 ) )
dup_vector( lines );
std::cout << lines.size( ) << " elements" << std::endl;
std::sort( lines.begin( ), lines.end( ) );
return 0;
}
これが行うことは、/etc/passwd を行のベクトルに読み込み、少なくとも 100 万エントリになるまでこのベクトルを何度も複製することです。std::move()
これは、最初の最適化が役立つ場所です。 に表示される明示的なものだけでなく、内部配列のサイズを変更 (新規作成 + コピー) する必要がある場合はdup_vector()
、push_back
それ自体のパフォーマンスが向上するはずです。
最後に、ベクトルがソートされます。2 つの要素が交換されるたびに一時オブジェクトをコピーする必要がない場合、これは間違いなく高速です。
これを 2 つの方法でコンパイルして実行します。1 つは C++98 として、もう 1 つは C++11 として (明示的な移動には -DCPP11 を使用)。
1> $ rm -f a.out ; g++ --std=c++98 test.cpp ; time ./a.out
2> $ rm -f a.out ; g++ --std=c++11 -DCPP11 test.cpp ; time ./a.out
3> $ rm -f a.out ; clang++ --std=c++98 test.cpp ; time ./a.out
4> $ rm -f a.out ; clang++ --std=c++11 -DCPP11 test.cpp ; time ./a.out
次の結果 (コンパイルごとに 2 回):
GCC C++98
1> real 0m9.626s
1> real 0m9.709s
GCC C++11
2> real 0m10.163s
2> real 0m10.130s
そのため、C++11 コードとしてコンパイルすると、実行が少し遅くなります。同様の結果はclangにも当てはまります:
clang C++98
3> real 0m8.906s
3> real 0m8.750s
clang C++11
4> real 0m8.858s
4> real 0m9.053s
誰かがこれがなぜなのか教えてもらえますか? コンパイラは、C++11 よりも前のバージョンでコンパイルする場合でも最適化が非常に優れているため、実際には移動セマンティック動作に到達しますか? を追加する-O2
と、すべてのコードがより高速に実行されますが、異なる標準間の結果は上記とほぼ同じです。
EDIT : std::string ではなく my::string を使用した新しい結果、およびより大きな個々の文字列:
$ rm -f a.out ; g++ --std=c++98 test.cpp ; time ./a.out
real 0m16.637s
$ rm -f a.out ; g++ --std=c++11 -DCPP11 test.cpp ; time ./a.out
real 0m17.169s
$ rm -f a.out ; clang++ --std=c++98 test.cpp ; time ./a.out
real 0m16.222s
$ rm -f a.out ; clang++ --std=c++11 -DCPP11 test.cpp ; time ./a.out
real 0m15.652s
C++98 と C+11 の間には、移動セマンティクスに関する非常に小さな違いがあります。GCC を使用した C++11 ではわずかに遅く、clang ではわずかに高速ですが、それでも非常に小さな違いがあります。
EDIT2:std::string
の COW がなくなると、パフォーマンスが大幅に向上します。
$ rm -f a.out ; g++ --std=c++98 test.cpp ; time ./a.out
real 0m10.313s
$ rm -f a.out ; g++ --std=c++11 -DCPP11 test.cpp ; time ./a.out
real 0m5.267s
$ rm -f a.out ; clang++ --std=c++98 test.cpp ; time ./a.out
real 0m10.218s
$ rm -f a.out ; clang++ --std=c++11 -DCPP11 test.cpp ; time ./a.out
real 0m3.376s
最適化を使用すると、違いも大きくなります。
$ rm -f a.out ; g++ -O2 --std=c++98 test.cpp ; time ./a.out
real 0m5.243s
$ rm -f a.out ; g++ -O2 --std=c++11 -DCPP11 test.cpp ; time ./a.out
real 0m0.803s
$ rm -f a.out ; clang++ -O2 --std=c++98 test.cpp ; time ./a.out
real 0m5.248s
$ rm -f a.out ; clang++ -O2 --std=c++11 -DCPP11 test.cpp ; time ./a.out
real 0m0.785s
上記は、C++11 で最大 6 ~ 7 倍高速であることを示しています。
素晴らしいコメントと回答をありがとう。この投稿が他の人にとっても有益で興味深いものになることを願っています。