web-dev-qa-db-ja.com

std :: vector reserve()とPush_back()はresize()と配列インデックスより高速です、なぜですか?

コードのブロックで簡単なパフォーマンステストを行っていました

_void ConvertToFloat( const std::vector< short >& audioBlock, 
                     std::vector< float >& out )
{
    const float rcpShortMax = 1.0f / (float)SHRT_MAX;
    out.resize( audioBlock.size() );
    for( size_t i = 0; i < audioBlock.size(); i++ )
    {
        out[i]  = (float)audioBlock[i] * rcpShortMax;
    }
}
_

65536のオーディオサンプルを処理するのにわずか1ミリ秒しかかからない、元の非常に単純な実装よりも高速であることに満足しています。

しかし、楽しみのために私は以下を試しました

_void ConvertToFloat( const std::vector< short >& audioBlock, 
                     std::vector< float >& out )
{
    const float rcpShortMax = 1.0f / (float)SHRT_MAX;
    out.reserve( audioBlock.size() );
    for( size_t i = 0; i < audioBlock.size(); i++ )
    {
        out.Push_back( (float)audioBlock[i] * rcpShortMax );
    }
}
_

これで、元のコードとまったく同じパフォーマンスが得られると思いました。しかし、突然、ループは900usecを使用します(つまり、他の実装よりも100usec速くなります)。

これによりパフォーマンスが向上する理由を誰かが説明できますか? resize()は、reserveが割り当てるだけで構成しない、新しく割り当てられたベクトルを初期化しますか?これは私が考えることができる唯一のものです。

PSこれは、シングルコア2Ghz AMD Turion 64 ML-37でテストされました。

42
Goz

リサイズは、reserveが割り当てるだけで構成しない、新しく割り当てられたベクトルを初期化しますか?

はい。

65
sepp2k

Resize()

コンテナを変更して、要素がちょうどn個になるようにします。必要に応じて、末尾に要素を挿入するか、末尾から要素を削除します。要素が挿入されている場合、それらはtのコピーです。 n > a.size()の場合、この式はa.insert(a.end(), n - size(), t)と同等です。 n < a.size()の場合、a.erase(a.begin() + n, a.end())と同等です。

予約()

Nがcapacity()以下の場合、この呼び出しは効果がありません。それ以外の場合は、追加メモリの割り当て要求です。リクエストが成功した場合、capacity()はn以上です。それ以外の場合、capacity()は変更されません。どちらの場合でも、size()は変更されません。

capacity() - size()を超える要素がベクターに挿入されると、メモリは自動的に再割り当てされます。再割り当てはsize()を変更せず、ベクトルの要素の値も変更しません。ただし、capacity()は増加します

予約は手動で再割り当てを引き起こします。 reserve()を使用する主な理由は効率です。ベクトルが最終的に増大する必要がある容量がわかっている場合、通常、自動再割り当てスキームに依存するよりも、一度にすべてのメモリを割り当てる方が効率的です。

5
Satbir

最初のコードは_out[i]_に書き込み、これはbegin() + iに要約されます(つまり、加算)。 2番目のコードは_Push_back_を使用します。これは、おそらくend()に相当する既知のポインターに即座に書き込みます(つまり、追加なし)。おそらく、整数インデックスではなくイテレータを使用することで、最初の実行を2番目の実行と同じくらい速くすることができます。

編集:他のコメントを明確にするためにも:ベクトルには浮動小数点が含まれており、浮動小数点の構築は事実上何もしません( "float f;"を宣言するのと同じ方法ではコードを発行せず、コンパイラーに通知するだけです)スタック上のフロートのためのスペースを節約するため)。したがって、浮動小数点数のベクトルに対するresize()reserve()のパフォーマンスの違いは、構築とは関係がないと思います。

3
AshleysBrain
_out.resize( audioBlock.size() );
_

outのサイズ(= 0)はaudioBlock.size()より小さいため、追加の要素が作成され、outの末尾に追加されます。これにより、デフォルトのコンストラクターが呼び出されて新しい要素が作成されます。

予約はメモリを割り当てるだけです。

1
aJ.