web-dev-qa-db-ja.com

c ++ std :: ostringstream vs std :: string :: append

何らかのバッファリングを使用するすべての例で、文字列ではなくストリームを使用していることがわかります。 std :: ostringstreamと<<演算子は、string.appendを使用するのとどのように異なりますか。どれがより速く、どれがより少ないリソース(メモリ)を使用します。

私が知っている違いの1つは、string :: appendが受け入れる制限されたタイプではなく、異なるタイプを出力ストリーム(整数など)に出力できることです。

以下に例を示します。

std::ostringstream os;
os << "Content-Type: " << contentType << ";charset=" << charset << "\r\n";
std::string header = os.str();

std::string header("Content-Type: ");
header.append(contentType);
header.append(";charset=");
header.append(charset);
header.append("\r\n");

ストリームの使用は明らかに短いですが、appendは文字列への参照を返すので、次のように記述できます。

std::string header("Content-Type: ");
header.append(contentType)
  .append(";charset=")
  .append(charset)
  .append("\r\n");

出力ストリームを使用すると、次のことができます。

std::string content;
...
os << "Content-Length: " << content.length() << "\r\n";

しかし、メモリの使用量と速度はどうですか?特に大きなループで使用する場合。

更新:

より明確な質問は次のとおりです:どちらを使用する必要があり、なぜですか?一方が優先される場合と他方が優先される場合がありますか?パフォーマンスとメモリの場合...まあ、ベンチマークが唯一の方法だと思いますすべての実装が異なる可能性があります。

更新2:

まあ私は答えから何を使うべきか明確なアイデアを得ることができません。つまり、それらのいずれかが仕事とベクトルを行うことを意味します。 Cubbiは、DietmarKühlを追加してNiceベンチマークを行いました。最大の違いは、これらのオブジェクトの構築です。答えを探しているなら、それもチェックすべきです。私は他の回答をもう少し待ちます(以前の更新を見てください)。もし私がそれを受け取らなければ、ベクトルを使用するという彼の提案がすでに行われているため、ベクトルがリソースを必要としないことを意味するため、トルガの回答を受け入れると思います。

29
NickSoft

_std::ostringstream_は、メモリ内の文字の連続した配列として格納されるとは限りません。実際には、これらのHTTPヘッダーを送信するときに文字の連続配列が必要になり、内部バッファーをコピー/変更してシーケンシャルにすることができます。

適切な_std::string_を使用する_std::string::reserve_には、この状況で_std::ostringstream_よりも遅く動作する理由はありません。

ただし、予約する必要があるサイズがまったくわからない場合は、_std::ostringstream_を追加する方がおそらく高速です。 _std::string_を使用して文字列が大きくなると、最終的にはバッファ全体の再割り当てとコピーが必要になります。 1つのstd::ostringstream::str()を使用して、他の方法で発生する複数の再割り当てと比較して、データを一度にシーケンシャルにすることをお勧めします。

追伸C++ 11より前の_std::string_もシーケンシャルである必要はありませんが、ほとんどすべてのライブラリはシーケンシャルとして実装しています。それを危険にさらすか、代わりに_std::vector<char>_を使用できます。追加を行うには、次を使用する必要があります。

_char str[] = ";charset=";
vector.insert(vector.end(), str, str + sizeof(str) - 1);
_

_std::vector<char>_は、おそらく構築する方が安価であるため、パフォーマンスには最適ですが、おそらく_std::string_および構築にかかる実際の時間と比較して重要ではありません。私はあなたがしようとしているものと似たようなことをして、以前に_std::vector<char>_を使いました。純粋に論理的な理由のため。ベクトルは仕事によく合っているように見えました。実際には、文字列操作などは必要ありません。また、後で行ったベンチマークにより、パフォーマンスが向上することが証明されました。または、_std::string_を使用して操作を十分に実装しなかったためである可能性があります。

選択中は、通常、ニーズと最小限の追加機能を備えたコンテナが最適です。

10
Etherealone

constructingストリームオブジェクトは、std::localeメンバーを保持する(したがって、構築する)必要があるため、文字列オブジェクトを構築するよりもはるかに複雑な操作です。とりわけ、状態を維持するために必要なものです(ただし、ロケールのマージンは最も大きくなります)。

追加も同様です。どちらも連続した文字の配列を維持し、容量を超えた場合はより多くを割り当てます。私が考えることができる唯一の違いは、ストリームに追加するときに、オーバーフローごとに1つの仮想メンバー関数呼び出しがあり(メモリの割り当て/コピーに加えて、オーバーフロー処理を支配します)、operator<<はいくつかの追加のチェックを行う必要があることですストリーム状態。

また、文字列全体をもう一度コピーするstr()を呼び出していることに注意してください。そのため、コードの実行内容に基づいて、ストリームの例はより多くの処理を行う必要があります。

テストしてみましょう:

#include <sstream>
#include <string>
#include <numeric>

volatile unsigned int sink;
std::string contentType(50, ' ');
std::string charset(50, ' ');
int main()
{
 for(long n = 0; n < 10000000; ++n)
 {
#ifdef TEST_STREAM    
    std::ostringstream os;
    os << "Content-Type: " << contentType << ";charset=" << charset << "\r\n";
    std::string header = os.str();
#endif
#ifdef TEST_STRING
    std::string header("Content-Type: ");
    header.append(contentType);
    header.append(";charset=");
    header.append(charset);
    header.append("\r\n");
#endif
    sink += std::accumulate(header.begin(), header.end(), 0);
 }
}

それは1000万繰り返しです

私のLinuxでは、

                   stream         string
g++ 4.8          7.9 seconds      4.4 seconds
clang++/libc++  11.3 seconds      3.3 seconds

したがって、このユースケースでは、これら2つの実装では、文字列はより速く動作するように見えますが、明らかに両方の方法で改善することがたくさんあります(reserve()文字列、ストリーム構築をループ外に移動し、必要のないストリームを使用するバッファにアクセスするためのコピーなど)

21
Cubbi

ストリームを使用すると、クラスMyclass<<操作をオーバーライドできるため、次のように記述できます。

MyClass x;
ostringstream y;
y << x;

文字列の追加機能をオーバーライドできないため、追加にはToStringメソッド(または同様のもの)が必要です。

一部のコードでは、より快適に感じるものを使用します。オブジェクトを単純にストリーミングできると便利な、より大きなプロジェクトにはストリームを使用します。

1
Sorin

速度が心配な場合は、プロファイリングやテストを行う必要があります。理論的には std::string::appendはより単純であるため、遅くはなりません(ストリームはロケール、異なるフォーマットを処理し、より汎用的でなければなりません)。しかし、1つのソリューションが実際にどれだけ高速であるかは、テストすることによってのみ実現できます。

0
Slava