2

ブーストドキュメントから 、

これにより、ほぼ最適なコードが生成されます。通常、BOOST_FOREACH のパフォーマンスは、同等の手作業でコーディングされたループの数パーセント以内です。

マクロと非標準の typeof 演算子を使用すると、まったく同等のものを生成できると思います。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 を指定します。ここで何か不足していますか?

4

5 に答える 5

5

Boost 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;
}

ループオーバーしたい可能性のあるものには、非常に多くの種類があります。C++11 では、ほとんどすべてをループできるため、これらすべてのトリックは不要になりました。

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

また

for(auto a=begin(something);a!=end(something);++i){  .. }
于 2012-02-21T12:20:53.577 に答える
2

あなたの好きなコンパイラに聞いてみませんか?

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

#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);
  }
}

これらのコマンドを使用して、LLVM IR を取得します。

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

これは簡単です:

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);
  }
}
于 2012-02-21T14:38:27.843 に答える
1

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

于 2012-02-21T12:14:45.963 に答える
0

主にエイリアシングが原因だと思います:(const)参照を使用すると、コンパイラーは、一部の変数がエイリアシングされていないことを理解するのが難しくなり、最適ではないコードが生成されます。

于 2012-12-12T07:33:55.383 に答える
0

ループを手作業でコーディングすると、反復子と範囲の既知の (必ずしもコンパイラまたは boost_foreach とは限らない) プロパティを利用できます。だから、あなたはおそらくもっとうまくやれるでしょう。

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

于 2012-02-21T12:21:08.270 に答える