2012-02-21 19 views
2

からboost doc,なぜBOOST_FOREACHは手作業のものとまったく同じではありませんか?

この結果、最適なコード生成が得られます。 BOOST_FOREACHのパフォーマンスは、通常、 の手作業でコーディングされたループの数パーセント以内です。

マクロと非標準型の演算子を使用していますが、私たちはまったく同じものを生成できます。 BOOST_FOREACHのどの機能が正確ではありませんか?

編集:

マイバージョン:

#define EACH(it,v) \ 
     for(typeof(v.begin()) it = v.begin();it != v.end(); ++it) 

//use this if you want a const_iterator from a non-const container 

    #define CONST_EACH(it,v) \ 
     typedef typeof(v) v_type; \ 
     typedef const v_type& const_type; \ 
     for(typeof(static_cast<const_type>(v).begin()) it = static_cast<const_type>(v).begin(); it != static_cast<const_type>(v).end(); ++it) 

私は任意のオーバーヘッドなしのバージョンを記述しようとしています。これは非標準型のtypeofを使用し、value_typeではなくiteratorを与えます。ここに何もないのですか?

+0

ちょうどノートでは、 'typeof'演算子は移植性がありません。 – fredoverflow

+2

ポータブルではないとはどういう意味ですか?私はそれが標準の一部ではないことを知っています。 – balki

+0

私はオプティマイザがそのステートメントに追いついているかもしれないと思います。 BOOST_FOREACHを使用して、同等のforループを使用してもう少しパフォーマンスを絞り込めるかどうかを確認するコードをベンチマークしました。 BOOST_FOREACHコードはわずかに速かった(つまり、おそらく誤差の範囲内) – Ferruccio

答えて

1

自然なループ構文をサポートするためにBOOST_FOREACHが使用するトリッキーの一部は、オブジェクトの余分なコピーを生成する可能性があると私は信じています。

5

ブーストforeachは些細ではありません。 gccで4.6:

int main() 
{ 
    std::string hello("Hello, world!"); 
    BOOST_FOREACH(char ch, hello) 
    { 
     std::cout << ch; 
    } 
    return 0; 
} 

は、A?B:Cでプローブされた多くのケースを生成します。

int main() 
{ 
    std::string hello("Hello, world!"); 

    if (
boost::foreach_detail_::auto_any_t _foreach_col9 = 
boost::foreach_detail_::contain((hello) , (true ? 0 : 
boost::foreach_detail_::or_( 
boost::foreach_detail_::and_( 
boost::foreach_detail_::not_(
boost::foreach_detail_::is_array_(hello)) , (true ? 0 : 
boost::foreach_detail_::is_rvalue_((true ? 
boost::foreach_detail_::make_probe(hello) : (hello)), 0))) , 
boost::foreach_detail_::and_( 
boost::foreach_detail_::not_(boost_foreach_is_noncopyable( 
boost::foreach_detail_::to_ptr(hello) , boost_foreach_argument_dependent_lookup_hack_value)) , boost_foreach_is_lightweight_proxy( 
boost::foreach_detail_::to_ptr(hello) , boost_foreach_argument_dependent_lookup_hack_value)))))) {} else if (
boost::foreach_detail_::auto_any_t _foreach_cur9 = 
boost::foreach_detail_::begin(_foreach_col9 , (true ? 0 : 
boost::foreach_detail_::encode_type(hello, 
boost::foreach_detail_::is_const_(hello))) , (true ? 0 : 
boost::foreach_detail_::or_( 
boost::foreach_detail_::and_( 
boost::foreach_detail_::not_(
boost::foreach_detail_::is_array_(hello)) , (true ? 0 : 
boost::foreach_detail_::is_rvalue_((true ? 
boost::foreach_detail_::make_probe(hello) : (hello)), 0))) , 
boost::foreach_detail_::and_( 
boost::foreach_detail_::not_(boost_foreach_is_noncopyable( 
boost::foreach_detail_::to_ptr(hello) , boost_foreach_argument_dependent_lookup_hack_value)) , boost_foreach_is_lightweight_proxy( 
boost::foreach_detail_::to_ptr(hello) , boost_foreach_argument_dependent_lookup_hack_value)))))) {} else if (
boost::foreach_detail_::auto_any_t _foreach_end9 = 
boost::foreach_detail_::end(_foreach_col9 , (true ? 0 : 
boost::foreach_detail_::encode_type(hello, 
boost::foreach_detail_::is_const_(hello))) , (true ? 0 : 
boost::foreach_detail_::or_( 
boost::foreach_detail_::and_( 
boost::foreach_detail_::not_(
boost::foreach_detail_::is_array_(hello)) , (true ? 0 : 
boost::foreach_detail_::is_rvalue_((true ? 
boost::foreach_detail_::make_probe(hello) : (hello)), 0))) , 
boost::foreach_detail_::and_( 
boost::foreach_detail_::not_(boost_foreach_is_noncopyable( 
boost::foreach_detail_::to_ptr(hello) , boost_foreach_argument_dependent_lookup_hack_value)) , boost_foreach_is_lightweight_proxy( 
boost::foreach_detail_::to_ptr(hello) , boost_foreach_argument_dependent_lookup_hack_value)))))) {} else for (bool _foreach_continue9 = true; _foreach_continue9 && ! 
boost::foreach_detail_::done(_foreach_cur9 , _foreach_end9 , (true ? 0 : 
boost::foreach_detail_::encode_type(hello, 
boost::foreach_detail_::is_const_(hello)))); _foreach_continue9 ? 
boost::foreach_detail_::next(_foreach_cur9 , (true ? 0 : 
boost::foreach_detail_::encode_type(hello, 
boost::foreach_detail_::is_const_(hello)))) : (void)0) if (
boost::foreach_detail_::set_false(_foreach_continue9)) {} else for (char ch = 
boost::foreach_detail_::deref(_foreach_cur9 , (true ? 0 : 
boost::foreach_detail_::encode_type(hello, 
boost::foreach_detail_::is_const_(hello)))); !_foreach_continue9; _foreach_continue9 = true) 
    { 
     std::cout << ch; 
    } 

    return 0; 
} 

ループする可能性のある種類のものがたくさんあります。あなたの手のコードをお使いのループ場合は

for(auto const &a: something){ .. } 

または

for(auto a=begin(something);a!=end(something);++i){ .. } 
+1

しかし、そのほとんどは、コンパイラがテンプレート –

+0

で何をすべきかを考え出した後、ヌルコードとして終わるでしょう。とにかく、上記は何が起こっているかを示しています。 –

+0

はい、私はそれがはるかに自明ではないことを理解しています。私の質問はそれがなぜそうでなければならないのですか?オーバーヘッドがなくても簡単なのはなぜですか? – balki

0

とほとんど何をループ、あなたがすることが知られを活用することができますすることができますように、C++ 11では、すべてのこれらのトリックは、もはや必要とされていませんイテレータと範囲の(ただし必ずしもコンパイラまたはboost_foreachの)プロパティは必要ありません。あなたはおそらくもっとうまくいくでしょう。

コンパイル時にクラスの特定のプロパティを検出できることにも大きく依存しています(コンパイラが使用するテンプレートメカニズムをサポートできない場合)。これは実行時まで延期する必要があります。これは、明らかに、あなたが知っている可能性が高いハンドコーディングで得られる結果ほど効率的ではありません。

2

お気に入りのコンパイラに質問してみませんか?簡単なために与え

~/projects$ clang++ -O2 -c -I/usr/lib/Boost/1-39-0-1/include/ test.cpp -emit-llvm 
~/projects$ llvm-dis test.o -show-annotations 

::私たちはLLVM IR取得これらのコマンドで

#include <cstring> 
#include <cstdio> 

#include <boost/foreach.hpp> 

char const* HelloWorld = "Hello, world!\n"; 

void simplefor() { 
    for(char const* it = HelloWorld, *end = HelloWorld + strlen(HelloWorld); 
     it != end; 
     ++it) 
    { 
    printf("%c", *it); 
    } 
} 

void foreach() { 
    BOOST_FOREACH(char ch, HelloWorld) 
    { 
    printf("%c", ch); 
    } 
} 

define void @_Z9simpleforv() nounwind uwtable { 
    %1 = load i8** @HelloWorld, align 8, !tbaa !0 ; [#uses=3 type=i8*] 
    %2 = tail call i64 @strlen(i8* %1) nounwind readonly ; [#uses=2 type=i64] 
    %3 = getelementptr inbounds i8* %1, i64 %2  ; [#uses=1 type=i8*] 
    %4 = icmp eq i64 %2, 0       ; [#uses=1 type=i1] 
    br i1 %4, label %._crit_edge, label %.lr.ph 

.lr.ph:           ; preds = %.lr.ph, %0 
    %it.01 = phi i8* [ %7, %.lr.ph ], [ %1, %0 ] ; [#uses=2 type=i8*] 
    %5 = load i8* %it.01, align 1, !tbaa !1   ; [#uses=1 type=i8] 
    %6 = sext i8 %5 to i32       ; [#uses=1 type=i32] 
    %putchar = tail call i32 @putchar(i32 %6) nounwind ; [#uses=0 type=i32] 
    %7 = getelementptr inbounds i8* %it.01, i64 1 ; [#uses=2 type=i8*] 
    %8 = icmp eq i8* %7, %3       ; [#uses=1 type=i1] 
    br i1 %8, label %._crit_edge, label %.lr.ph 

._crit_edge:          ; preds = %.lr.ph, %0 
    ret void 
} 

私たちは(混乱を避けるために)簡単なテストケースを使用してみましょう

およびBOOST_FOREACHの場合:

; [#uses=0] 
define void @_Z7foreachv() nounwind uwtable { 
    %1 = load i8** @HelloWorld, align 8, !tbaa !0 ; [#uses=1 type=i8*] 
    br label %2 

; <label>:2          ; preds = %.preheader, %0 
    %.in = phi i8* [ %6, %.preheader ], [ %1, %0 ] ; [#uses=2 type=i8*] 
    %3 = load i8* %.in, align 1, !tbaa !1   ; [#uses=2 type=i8] 
    %4 = icmp eq i8 %3, 0       ; [#uses=1 type=i1] 
    br i1 %4, label %.critedge, label %.preheader 

.preheader:          ; preds = %2 
    %5 = sext i8 %3 to i32       ; [#uses=1 type=i32] 
    %putchar = tail call i32 @putchar(i32 %5) nounwind ; [#uses=0 type=i32] 
    %6 = getelementptr inbounds i8* %.in, i64 1  ; [#uses=1 type=i8*] 
    br label %2 

.critedge:          ; preds = %2 
    ret void 
} 

私はシンプルなケースではなく、より少ないブランチ(2つではなく反復ごとに1つ)について説明していますが、そこからパフォーマンスをピン止めするのは難しいでしょう。

もちろん...それはもはや重要ではありません!雹C++ 11:あなたは(定数)参照を使用する場合、コンパイラは、いくつかの変数がないエイリアスを行うことを考え出す難しく時間を持っており、より少ない生成:

void bestfor() { 
    for(char const ch: HelloWorld) { 
    printf("%c", ch); 
    } 
} 
0

私はそれが主な理由エイリアシングのだと思います最適なコード。

関連する問題