コードのブロックで簡単なパフォーマンステストを行っていました
_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でテストされました。
リサイズは、reserveが割り当てるだけで構成しない、新しく割り当てられたベクトルを初期化しますか?
はい。
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()
を使用する主な理由は効率です。ベクトルが最終的に増大する必要がある容量がわかっている場合、通常、自動再割り当てスキームに依存するよりも、一度にすべてのメモリを割り当てる方が効率的です。
最初のコードは_out[i]
_に書き込み、これはbegin() + i
に要約されます(つまり、加算)。 2番目のコードは_Push_back
_を使用します。これは、おそらくend()
に相当する既知のポインターに即座に書き込みます(つまり、追加なし)。おそらく、整数インデックスではなくイテレータを使用することで、最初の実行を2番目の実行と同じくらい速くすることができます。
編集:他のコメントを明確にするためにも:ベクトルには浮動小数点が含まれており、浮動小数点の構築は事実上何もしません( "float f;"を宣言するのと同じ方法ではコードを発行せず、コンパイラーに通知するだけです)スタック上のフロートのためのスペースを節約するため)。したがって、浮動小数点数のベクトルに対するresize()
とreserve()
のパフォーマンスの違いは、構築とは関係がないと思います。
_out.resize( audioBlock.size() );
_
out
のサイズ(= 0)はaudioBlock.size()
より小さいため、追加の要素が作成され、out
の末尾に追加されます。これにより、デフォルトのコンストラクターが呼び出されて新しい要素が作成されます。
予約はメモリを割り当てるだけです。