std :: string_viewはconst std :: string&よりどのくらい正確に高速ですか?


221

std::string_viewはC ++ 17に対応しており、の代わりに使用することをお勧めしますconst std::string&

その理由の1つはパフォーマンスです。

誰かが、パラメータタイプとして使用した場合よりも正確に std::string_view、または高速になると説明できますかconst std::string&?(呼び出し先にコピーが作成されていないと仮定しましょう)


7
std::string_view(char * begin、char * end)ペアの単なる抽象化です。std::string不要なコピーを作成するときに使用します。
QuestionC

私の意見では、問題はどちらが速いかではなく、いつ使用するかです。文字列を操作する必要があり、それが永続的でないか、元の値を保持する場合、文字列のコピーを作成する必要がないため、string_viewは完璧です。しかし、たとえばstring :: findを使用して文字列の何かをチェックするだけでよい場合は、参照の方が適しています。
TheArquitect 2017

@QuestionCは、APIを制限したくない場合に使用しますstd::string(string_viewは、生の配列、ベクター、std::basic_string<>デフォルト以外のアロケーターなどを受け入れることができます。その他、明らかにstring_views)
sehe

回答:


213

std::string_view いくつかのケースではより高速です。

まず、std::string const&データがでありstd::string、生のC配列ではなく、char const*C APIによって返されたstd::vector<char>、一部の逆シリアル化エンジンによって生成されたなどである必要があります。回避された形式変換は、バイトのコピーを回避し、(文字列が特定のstd::string実装のSBO implementation)は、メモリ割り当てを回避します。

void foo( std::string_view bob ) {
  std::cout << bob << "\n";
}
int main(int argc, char const*const* argv) {
  foo( "This is a string long enough to avoid the std::string SBO" );
  if (argc > 1)
    foo( argv[1] );
}

string_viewケースでは割り当ては行われませんfooが、のstd::string const&代わりにが取られた場合はありstring_viewます。

2番目の本当に大きな理由は、コピーなしで部分文字列を操作できることです。2ギガバイトのjson文字列(!)²を解析するとします。それをに解析するとstd::string、ノードの名前または値を格納する各解析ノードは、2 GBの文字列からローカルノードに元のデータをコピーします。

代わりに、それをstd::string_viewsに解析すると、ノード元のデータを参照します。これにより、数百万の割り当てを節約し、解析中のメモリ要件を半分にすることができます。

あなたが得ることができるスピードアップはばかげています。

これは極端なケースですが、他の「部分文字列を取得して操作する」ケースでも、を使用して適切なスピードアップを生成できstring_viewます。

決定の重要な部分は、を使用することで失うものですstd::string_view。それは多くはありませんが、それは何かです。

暗黙のnull終了を失う、それはそれについてです。そのため、同じ文字列が3つの関数に渡され、そのすべてにnullターミネータが必要な場合、std::string一度に変換するのが賢明です。したがって、コードにnullターミネーターが必要であることがわかっていて、Cスタイルのソースバッファーなどからの文字列を期待しない場合は、を使用することができますstd::string const&。それ以外の場合はstd::string_view

std::string_viewnullで終了した(またはより洗練された)かどうかを示すフラグがある場合は、を使用する最後の理由も削除されますstd::string const&

std::stringなしconst&でa をとることがに最適な場合がありstd::string_viewます。呼び出し後に文字列のコピーを無期限に所有する必要がある場合は、値渡しを行うのが効率的です。SBOの場合(割り当てはなく、複製するための数文字のコピーのみ)、またはヒープが割り当てられたバッファーをローカルに移動できますstd::string。2つのオーバーロードを持つstd::string&&std::string_viewより高速かもしれませんが、わずかに、それは(あなたの速度の向上のすべてを要することができる)ささやかなコードの膨張を引き起こします。


¹小さなバッファーの最適化

²実際の使用例。


8
また、所有権も失います。どの文字列が返されると、それは場合にのみ関心があるかもしれない十分な長さを生き残るために保証されているバッファの部分文字列以外に何もする必要があります。実際、所有権の喪失は非常に両刃の武器です。
デュプリケータ

SBOが奇妙に聞こえます。私はいつもSSO(小さな文字列の最適化)を聞いたことがあります
phuclv

@phu確かに; しかし、トリックを使用するのは文字列だけではありません。
Yakk-Adam Nevraumont

@phuclv SSOはSBOの特定のケースにすぎず、小さなバッファーの最適化を表しています。代わりの用語は、小さなデータoptです。小さなオブジェクトの選択。、または小さなサイズのオプション。
Daniel Langr

59

string_viewがパフォーマンスを向上させる1つの方法は、プレフィックスとサフィックスを簡単に削除できることです。内部的には、string_viewは、文字列バッファへのポインタに接頭辞サイズを追加するか、バイトカウンタから接尾辞サイズを差し引くだけです。これは通常、高速です。一方、std :: stringは、substrのようなことをするときにそのバイトをコピーする必要があります(この方法では、バッファーを所有する新しい文字列を取得しますが、多くの場合、コピーせずに元の文字列の一部を取得するだけです)。例:

std::string str{"foobar"};
auto bar = str.substr(3);
assert(bar == "bar");

std :: string_view:

std::string str{"foobar"};
std::string_view bar{str.c_str(), str.size()};
bar.remove_prefix(3);
assert(bar == "bar");

更新:

私はいくつかの実数を加えるために非常に単純なベンチマークを書きました。素晴らしいgoogleベンチマークライブラリを使用しました。ベンチマーク機能は次のとおりです。

string remove_prefix(const string &str) {
  return str.substr(3);
}
string_view remove_prefix(string_view str) {
  str.remove_prefix(3);
  return str;
}
static void BM_remove_prefix_string(benchmark::State& state) {                
  std::string example{"asfaghdfgsghasfasg3423rfgasdg"};
  while (state.KeepRunning()) {
    auto res = remove_prefix(example);
    // auto res = remove_prefix(string_view(example)); for string_view
    if (res != "aghdfgsghasfasg3423rfgasdg") {
      throw std::runtime_error("bad op");
    }
  }
}
// BM_remove_prefix_string_view is similar, I skipped it to keep the post short

結果

(x86_64 linux、gcc 6.2、 " -O3 -DNDEBUG"):

Benchmark                             Time           CPU Iterations
-------------------------------------------------------------------
BM_remove_prefix_string              90 ns         90 ns    7740626
BM_remove_prefix_string_view          6 ns          6 ns  120468514

2
実際のベンチマークを提供してくれてうれしいです。これは、関連するユースケースで何が得られるかを本当に示しています。
Daniel Kamil Kozar、2016年

1
@DanielKamilKozarフィードバックをありがとう。また、ベンチマークは価値があると思います。
Pavel Davydov 2016年

47

主な理由は2つあります。

  • string_view 既存のバッファ内のスライスであり、メモリ割り当ては必要ありません
  • string_view 参照ではなく値で渡される

スライスを持つことの利点は複数あります:

  • 新しいバッファを割り当てるchar const*かどうかに関係char[]なく使用できます
  • 割り当てることなく、複数のスライスとサブスライスを既存のバッファに取り込むことができます
  • サブストリングはO(1)であり、O(N)ではありません
  • ...

全体的に、より良い、より一貫したパフォーマンス。


値による受け渡しには、エイリアスによるため、参照による受け渡しよりも利点があります。

具体的には、 std::string const&パラメータ、参照文字列が変更されないという保証はありません。その結果、コンパイラーは、不透明なメソッド(データ、長さなどへのポインター)を呼び出すたびに、文字列の内容を再フェッチする必要があります。

一方、string_viewby値を渡す場合、コンパイラーは静的に、他のコードが現在スタック(またはレジスター)にある長さとデータポインターを変更できないと判断できます。その結果、関数呼び出し全体でそれらを「キャッシュ」できます。


36

それができることの1つはstd::string、nullで終了する文字列からの暗黙的な変換の場合にオブジェクトを構築しないことです。

void foo(const std::string& s);

...

foo("hello, world!"); // std::string object created, possible dynamic allocation.
char msg[] = "good morning!";
foo(msg); // std::string object created, possible dynamic allocation.

12
string_viewの方がstring&よりも速くなることはconst std::string str{"goodbye!"}; foo(str);おそらくないでしょう
Martin Bonnerはモニカ

1
文句を言わないstring_view、それは中1つのポインタとは対照的に、二つのポインタをコピーする必要があるため、遅くなりますかconst string&
Balki 2017

9

std::string_view基本的にはのラッパーconst char*です。また、渡すconst char*ことは、渡すconst string*(またはconst string&)と比較して、システム内のポインタが1つ少ないことをstring*意味します。

string* -> char* -> char[]
           |   string    |

明らかにconst引数を渡すために、最初のポインタは不必要です。

psの間の1つのsubstancial違いstd::string_viewconst char*、それにもかかわらず、string_viewsがnullで終わる(彼らが内蔵されているサイズ)である必要はありませんということであり、これは長い文字列のランダムなインプレーススプライシングすることができます。


4
反対票とは何ですか?std::string_viewsは単なる派手なconst char*s、期間です。このようなGCCが実装して:class basic_string_view {const _CharT* _M_str; size_t _M_len;}
n.caillou

4
(現在の65から)65K担当者に連絡すれば、これが承認された回答になります(cargo-cult群衆への波):)
mlvljr

7
@mlvljr誰も渡さないstd::string const*。そしてその図は理解できません。@ n.caillou:自分のコメントは回答よりも正確です。それはstring_view「空想char const*」以上のものになります-それは本当にかなり明白です。
sehe

@sehe誰も問題ない(つまり、const文字列へのポインタ(または参照)を渡すのはなぜですか?):)
mlvljr

2
@seheあなたは、最適化や実行の観点からこれを理解して、std::string const*そしてstd::string const&あなたは、同じではないんですか?
n.caillou 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.