BOOST_FOREACHのパフォーマンス

(boost/)foreach.hpp の、一番最後のコードの意味がわかりそうな予感がするコードを書いてみた。ようするにこういうことだ。

vector< int > iv;
for( int i=0; i<10000; ++i ){
	iv.push_back( i );
}// i
// というコンテナがあったとしてー、BOOST_FOREACHのコードの中で、
// 実際マクロになっているのは、以下の4行にあたるような処理なのである。
if( bool cont = !0 )
for( vector< int >::iterator it=iv.begin(); it!=iv.end(); ++it )	
	if( cont = !0 )
	for( int i= *it ; cont; cont=0 )
		// 処理の中身
		cout<< "hi"<< i <<endl;

実際の、(boost/)foreach.hpp の、一番最後のコードでは、上記コードの「処理の中身」より上の4行にあたる部分が、全てマクロとして宣言されている。もちろん、実際には、こんな簡単であろうはずもなく、任意型&任意反復子型のオンパレードだ。が、とにかく最終的な一番最後のfor文の構造としては、簡略化してみれば、まさにこうなっているのである。
一見二重ループに見える、いや、実際二重ループになってはいるが、内側のループは、1呼び出しあたり1回しか回らないようになっている。実際に10000回っているのは、外側のループだ。そのつど内側のループが1回ずつ呼ばれ、結局「処理の中身」が正味で10000回呼ばれるというしくみだ。(わかるとは思うが「処理の中身」以下は、BOOST_FOREACHの利用者が記述するループの中身の処理にあたる。たいていの場合{}でスコープを作ったりして利用するわけだ。)
この内側のfor文は一体、何をやっているのだろうか?それは、ループの中で、ループの要素アイテムとして、「ユーザーが指定した型&変数名」が使えるようにするため(多分それだけのため)のしかけだ。実際にはこの内側のfor文の中では、BOOST_FOREACH内部の任意コンテナ型を、ユーザ指定の変数にキャストする処理が入っている。全くよく考えたものだ、といったところである。
また、最初のif文は何をやっているかわかるだろうか?目的はもちろん条件判定ではない。たんにその寿命がスコープ内で自動的に切れる変数を宣言したいだけだ。では、2番目のif文はどうか?これも単に、一連のスコープ繋がりに切れ目を入れることなくcontに値を代入したいがために、そう書いてあるだけだ。これら自明のif文では動的な条件判定はされず、静的にコンパイル時に行われるものと祈りたい。
(これだとループ内部のbreakやcontinueが、最も内側のループによってかき消されてしまうので、そこはちゃんとbreakやcontinue も正しく使えるように、工夫しなくてはならなかったりするわけだ。ここでは触れないけどね。)
また、内側のforの中には、つまりそれがforであるが故に、条件判定がまた一つあるわけで、つまり、1要素の処理の呼び出しあたり、いちいち余計な条件判定が走っているかも知れないということだ。だから、BOOST_FOREACH() は、(誰が見たって)通常のfor文記述より確実に遅いに違いない。そこで、ちょっとした実験を行ってみた。具体的にはi=0から10000まで、iの2乗値の和を計算するだけの(極めて軽い)処理である。実験の結果から言うと、for文だと21ms かかるところを、BOOST_FOREACH()だと、29ms かかった。
ただし、もし、for文を使ったループが、

for( it=iv.begin(); it!=iv.end(); ++i ).. // 51msかかる!

などと書かれていたならば、それは、残念ながらBOOST_FOREACHの方が速い。何故だかおわかりだろうか?上記の書き方は、毎ループでend()式を評価してしまっている。これに対して、BOOST_FOREACH()の中では、end() 式の評価されるのは、一度だけだ。BOOST_FOREACH()よりも高速なfor文とは、あくまで、

vector< int >::iterator ie = iv.end;
for( it=iv.begin(); it!=ie; ++i ).. // 21ms

と書いた場合である。しかし、こんな書き方、いちいちしてられるだろうか?と思ってしまうのだが、いかがだろうか?あるいは独自のマクロ記述を考案する?
以上の考察により、普通のfor文ループとBOOST_FOREACHのパフォーマンスの比較を見た。
最も悲劇的なのは、BOOST_FOREACH()を使わない、STLの基本に則した、なおかつパフォーマンスも遅い、上記の51msかかる書き方で、自分のソースが埋め尽くされていた場合だ。気をつけたい。(x_x)っていうかそういう書き方が一番多いと思われるので、今すぐにでもBOOST_FOREACHを使うか、その代わりとなるマクロを自分で勉強して作って、それを使うようにするかした方がいいんじゃないか?という噂もある。
って思ったんだけども、ループの中で要素を削除する処理が入るような場合は、遅くても上記の51msの書き方で書くしかないんだけどね。もちろんその場合は、BOOST_FOREACHは使えないだろうし。
また、当然だが、ループの中の処理コストが高ければ高いほど、ループを何で回すのかといった問題は、全体のパフォーマンスの中でみれば、無視できて、実質気にする必要は全くない、という場合もあるだろうから、比較的軽い処理のループをたくさん回さねばならない場合にのみ、このような心配をする意味がある、と言えそうだ。