vector, list, set, dequeのパフォーマンスを実験したよ

やあ子供たち。今日はSTLのstd::⇒ vector, list, set, dequeのパフォーマンス測定の実験をしたよ。以下のようなコードだ。試してみてくれ。

#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#include <ctime>
#include <set>
#include <deque>
using namespace std;

#define N 1000000
#define P  400000

class less_than_4000
{ 
public: bool operator()( int i ){ return i<P; }
};

int main( int argc, char* argv[] )
{
vector< int > vec;
list< int > lst;
set< int > st;
deque< int > qu;	

clock_t c0 = clock();
for( int i=N; i>=0; --i ){ vec.push_back( i ); } clock_t c1 = clock();
for( int i=N; i>=0; --i ){ lst.push_back( i ); } clock_t c2 = clock();
for( int i=N; i>=0; --i ){ st.insert( i ); }	 clock_t c3 = clock();
for( int i=N; i>=0; --i ){ qu.push_back( i ); }	 clock_t c4 = clock();


clock_t s0 = clock();
lst.sort();                    clock_t s1 = clock();
sort( vec.begin(), vec.end() );clock_t s2 = clock();
sort( qu.begin(), qu.end() );  clock_t s3 = clock();

clock_t f0 = clock();
{
	for( int j=0; j<1000; ++j )
	vector< int >::iterator it = find( vec.begin(), vec.end(), 8888+j );
}
clock_t f1 = clock();
{
for( int j=0; j<1000; ++j )
	list< int >::iterator it = find( lst.begin(), lst.end(), 8888+j );
}
clock_t f2 = clock();
{
	for( int j=0; j<1000; ++j )
	deque< int >::iterator it = find( qu.begin(), qu.end(), 8888+j );
}
clock_t f3 = clock();

clock_t fe0 = clock();
{
	for( int j=0; j<1000; ++j ){
	vector< int >::iterator it = find( vec.begin(), vec.end(), 8888+j );
	vec.erase( it );
	}
}
clock_t fe1 = clock();
{
	for( int j=0; j<1000; ++j ){
	list< int >::iterator it = find( lst.begin(), lst.end(), 8888+j );
	lst.erase( it );
	}
}
clock_t fe2 = clock();
{
	for( int j=0; j<1000; ++j ){
	deque< int >::iterator it = find( qu.begin(), qu.end(), 8888+j );
	qu.erase( it );
	}
}
clock_t fe3 = clock();
{
	for( int j=0; j<1000; ++j ){
	set< int >::iterator it = st.find( 8888+j );
	st.erase( it );
	}
}
clock_t fe4 = clock();
clock_t i0 = clock();
{
	vector< int >::iterator it = find( vec.begin(), vec.end(), 8888 );
	for( int j=0; j<1000; ++j ){
	vec.insert( it, j );
	}
}
clock_t i1 = clock();
{
	list< int >::iterator it = find( lst.begin(), lst.end(), 8888 );
	for( int j=0; j<1000; ++j ){
	lst.insert( it, j );
	}
}
clock_t i2 = clock();
{
	deque< int >::iterator it = find( qu.begin(), qu.end(), 8888 );
	for( int j=0; j<1000; ++j ){
	qu.insert( it, j );
	}
}
clock_t i3 = clock();
{
	set< int >::iterator it = st.find( 8888 );
	for( int j=0; j<1000; ++j ){
	st.insert( it, j );
	}
}
clock_t i4 = clock();

clock_t t0 = clock();
{
	for( int j=0; j<1000; ++j )
	  vector< int >::iterator it = lower_bound( vec.begin(), vec.end(), 8888 );
}
clock_t t1 = clock();
{
	for( int j=0; j<1000; ++j )
	list< int >::iterator it = lower_bound( lst.begin(), lst.end(), 8888 );
}
clock_t t2 = clock();
{
	for( int j=0; j<1000; ++j )
	set< int >::iterator it = st.find( 8888 );
}
clock_t t3 = clock();
{
	for( int j=0; j<1000; ++j )
	deque< int >::iterator it = find( qu.begin(), qu.end(), 8888 );
}
clock_t t4 = clock();
clock_t r0 = clock();
vec.erase( remove_if( vec.begin(), vec.end(), less_than_4000() ), vec.end() ); 
clock_t r1 = clock();
lst.remove_if( less_than_4000() );                     clock_t r2 = clock();
qu.erase( remove_if( qu.begin(), qu.end(), less_than_4000() ), qu.end() );   
clock_t r3 = clock();

const float cps = static_cast< const float >( CLOCKS_PER_SEC );
cout << N << " elems." << endl <<endl;
cout << "setup vector:  " << (c1-c0)/cps << endl;
cout << "setup list:    " << (c2-c1)/cps << endl;
cout << "setup set:     " << (c3-c2)/cps << endl;
cout << "setup deque:   " << (c4-c3)/cps << endl;
cout << endl;

cout << "sort vector:   " << (s1-s0)/cps << endl;
cout << "sort list:     " << (s2-s1)/cps << endl;
cout << "sort deque:    " << (s3-s2)/cps << endl;
cout << endl;

cout << "1000 times find() vector: " << (f1-f0)/cps << endl;
cout << "1000 times find() list    " << (f2-f1)/cps << endl;
cout << "1000 times find set       " << (t3-t2)/cps << endl;
cout << "1000 times find deque     " << (t4-t3)/cps << endl;
cout << endl;

cout << "1000 times {find() and erase()} vector: " << (fe1-fe0)/cps << endl;
cout << "1000 times {find() and erase()} list    " << (fe2-fe1)/cps << endl;
cout << "1000 times {find() and erase()} set   " << (fe4-fe3)/cps << endl;
cout << "1000 times {find() and erase()} deque   " << (fe3-fe2)/cps << endl;
cout << endl;

cout << "1000 times insert() vector: " << (i1-i0)/cps << endl;
cout << "1000 times insert() list    " << (i2-i1)/cps << endl;
cout << "1000 times insert() set     " << (i4-i3)/cps << endl;
cout << "1000 times insert() deque   " << (i3-i2)/cps << endl;
cout << endl;

cout << "1000 times lower_bound() vector: " << (t1-t0)/cps << endl;
cout << "1000 times lower_bound() list    " << (t2-t1)/cps << endl;
cout << "1000 times lower_bound() deque    " << (t4-t3)/cps << endl;
cout << endl;

cout << "remove if<"<<P<<" vector:   " << (r1-r0)/cps << endl;
cout << "remove if<"<<P<<" list:     " << (r2-r1)/cps << endl;
cout << "remove if<"<<P<<" deque:    " << (r3-r2)/cps << endl;
cout << endl;

{
	vector< int > vec;
	set< int > st;
	for( int i=0; i<N; ++i ){
		vec.push_back( i );
		st.insert( i );
	}// i
	clock_t ri0 = clock();
	vec.erase( remove_if( vec.begin(), vec.end(), less_than_4000() ), vec.end() );
	clock_t ri1 = clock();
	for( int i=0; i<P; ++i ){
		set< int >::iterator it = st.find( i );
		if( it!= st.end() )
			st.erase( it );
	}// i
	clock_t ri2 = clock();
	cout<< st.size() <<" "<< vec.size()<<endl;
	cout << "remove if<"<<P<<" vector:    " << (ri1-ri0)/cps << endl;
	cout << "find <"<<P<<" and erase set: " << (ri2-ri1)/cps << endl;
}
return 0;
}


そして結果はこんな感じだ。

1000000 elems.

setup vector:  0.007
setup list:    0.063
setup set:     0.187
setup deque:   0.018

sort vector:   0.13
sort list:     0.015
sort deque:    0.172

1000 times find() vector: 0.005
1000 times find() list    0.015
1000 times find set       0
1000 times find deque     6.127

1000 times {find() and erase()} vector: 0.473
1000 times {find() and erase()} list    0.015
1000 times {find() and erase()} set   0.001
1000 times {find() and erase()} deque   0.137

1000 times insert() vector: 0.001
1000 times insert() list    0.004
1000 times insert() set     0
1000 times insert() deque   0.012

1000 times lower_bound() vector: 0
1000 times lower_bound() list    8.465
1000 times lower_bound() deque    6.127

remove if<400000 vector:   0.004
remove if<400000 list:     0.022
remove if<400000 deque:    0.015

600000 600000
remove if<400000 vector:    0.003
find <400000 and erase set: 0.044

どうかな?概ねコンテナ選びに迷ったらvectorにしとけばいいんじゃないかな。(ていうかinsertですらlistよりvectorの方が速え。うける!(←後日記:ごめん、これはadvanceが遅いだけだった。listのinsertだけなら0秒だった))これ以外にもいろいろ特性があるけれど、それぞれ一長一短だね。おじさんの場合は実際のところ、この中で言うと日ごろ使ってるのはvectorとsetくらいかなー。
1個1個の挿入や削除のスピードなんて、人間にはさほど体感でわからないし問題ではない。気にすべきは大量の要素数を一気に操作したい場合だ。そしてとくに大量の部分削除の性能が問題となることが多い。ここで特筆すべきは、vectorは、find して erase してると遅いけど、まとめて消すremove_if +eraseなら誰にも負けないくらい高速だということだ。あとはvectorはeraseは遅いがバカ検索の一発findだけだったら意外にlistやdequeより格段に速いというのも覚えているといい特性かも知れない。
更に言うと、結果出力の最後の2行のおまけ実験結果を見て欲しい。setは検索やeraseが速いからといって、後ろ40万要素を削除するのに各要素についていちいちfindしてはeraseする、なんてことをやったとしてもたしかに、0.04秒で終わるが、vectorでremove_ifした場合(0.003秒で終わる)に比べたら10倍以上遅いし、メモリだって食うし、最初の構築だって時間かかる。

特定の処理・アルゴリズムの中でsetを使うことはあったとしても、システムの根幹部分にあり、大量のデータを覚えさせておくコンテナにsetを使おうとは思わないというか、使いたくはないというのが、おじさんの感覚かな。場合にもよるだろうけど。

Effective STL―STLを効果的に使いこなす50の鉄則

Effective STL―STLを効果的に使いこなす50の鉄則

  • 作者: スコットメイヤーズ,Scott Meyers,細谷昭
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2002/01
  • メディア: 単行本
  • 購入: 9人 クリック: 155回
  • この商品を含むブログ (95件) を見る