365 votes

Comment exactement std :: string_view est plus rapide que const std :: string &?

std::string_view a été converti en C ++ 17 et il est généralement recommandé de l'utiliser au lieu de const std::string& .

Une des raisons est la performance.

Quelqu'un peut-il expliquer comment exactement std::string_view est / sera plus rapide que const std::string& lorsqu'il est utilisé comme type de paramètre? (supposons qu'aucune copie dans l'appelé ne soit faite)

327voto

Yakk Points 31636

std::string_view est plus rapide dans certains cas.

Tout d'abord, std::string const& besoin de données en std::string, et pas un C brutes tableau, une char const* retournée par l'API C, std::vector<char> produite par certains moteur de désérialisation, etc. L'économie de conversion de format évite la copie des octets, et (si la chaîne est plus longue que la SBO1 pour l' std::string mise en œuvre) permet d'éviter une allocation de mémoire.

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

Pas de dotations sont fait dans l' string_view des cas, mais il y en aurait si foo a eu un std::string const& au lieu de string_view.

La deuxième vraiment grande raison est qu'il permet de travailler avec des sous-chaînes sans une copie. Supposons que vous êtes l'analyse d'un 2 go de chaîne json (!)2. Si vous analysez en std::string, analyser nœud où ils stockent le nom ou la valeur d'un nœud copie les données d'origine à partir de la 2 go de chaîne à un nœud local.

Au lieu de cela, si vous l'analyser pour std::string_views, les nœuds se référer aux données originales. Cela peut sauver des millions de dotations et de réduire de moitié les besoins en mémoire lors de l'analyse.

Le speedup vous pouvez obtenir est tout simplement ridicule.

C'est un cas extrême, mais d'autres "obtenir une sous-chaîne et de travailler avec elle" cas peut aussi générer décent accélérations avec string_view.

Une partie importante de la décision est ce que vous perdez en utilisant std::string_view. Il n'est pas beaucoup, mais c'est quelque chose.

Vous perdez implicite de fin null, et c'est à son sujet. Donc, si la même chaîne sera passé à 3 fonctions qui nécessitent tous un terminateur null, la conversion en std::string une fois peut être sage. Ainsi, si votre code est connu pour avoir besoin d'un terminateur null, et vous ne vous attendez pas les chaînes de la fed à partir de C-style provenant des tampons ou le genre, peut-être prendre un std::string const&. Sinon, prendre un std::string_view.

Si std::string_view avaient un drapeau qui dit si c'était une valeur null (ou quelque chose de fantaisiste), il allait supprimer même le dernier de raison d'utiliser un std::string const&.

Il y a un cas où la prise d' std::string sans const& est optimal plus d'un std::string_view. Si vous avez besoin d'une copie de la chaîne indéfiniment après l'appel, en prenant en valeur efficace. Vous allez être dans le SBO cas (et pas de dotations, à seulement quelques copies de caractères pour le dupliquer), ou vous serez en mesure de déplacer le segment de mémoire-tampon alloué dans un local std::string. Avoir deux surcharges std::string&& et std::string_view pourrait être plus rapide, mais seulement légèrement, et elle serait la cause de modeste augmentation du code (qui pourrait vous coûter de tous les gains de vitesse).


1 Petit Tampon De L'Optimisation

2 Réel de cas d'utilisation.

100voto

Pavel Davydov Points 811

Une façon de string_view améliore les performances, c'est qu'il permet d'éliminer les préfixes et les suffixes facilement. Sous le capot, string_view suffit d'ajouter le préfixe de la taille d'un pointeur vers une chaîne de caractères de la mémoire tampon, ou soustraire le suffixe de la taille de l'octet contre, ce n'est généralement rapide. std::string sur l'autre main a copier ses octets quand vous faites quelque chose comme substr (de cette façon vous obtenez une nouvelle chaîne qui est propriétaire de son tampon, mais dans de nombreux cas, vous voulez juste pour obtenir une partie de la chaîne d'origine sans les copier). Exemple:

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

Avec std::string_view:

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

Mise à jour:

J'ai écrit un très simple référence à ajouter un peu de nombres réels. J'ai utilisé génial google de référence de la bibliothèque. Comparé fonctions sont les suivantes:

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

Résultats

(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

65voto

Matthieu M. Points 101624

Il y a 2 raisons principales:

  • string_view est une tranche dans un tampon, il ne nécessite pas d'allocation de mémoire
  • string_view est passé par valeur, et non par référence

Les avantages d'avoir une tranche sont multiples:

  • vous pouvez l'utiliser avec d' char const* ou char[] sans allouer un nouveau tampon
  • vous pouvez prendre plusieurs tranches et les sous-tranches dans un tampon sans allocation
  • la sous-chaîne est O(1), pas de O(N)
  • ...

Mieux et plus cohérente de la performance de tous.


Passage par valeur présente également des avantages sur le passage par référence, à cause de l'aliasing.

Plus précisément, lorsque vous avez un std::string const& paramètre, il n'y a aucune garantie que la chaîne de référence ne sera pas modifié. En conséquence, le compilateur doit re-récupérer le contenu de la chaîne après chaque appel dans une opaque méthode (pointeur de données, durée, ...).

D'autre part, lors du passage d'un string_view , en valeur, le compilateur peut déterminer statiquement qu'aucun autre code peut modifier la longueur et les pointeurs de données maintenant sur la pile (ou dans les registres). En conséquence, il peut "cache" à travers des appels de fonction.

41voto

juanchopanza Points 115680

Une chose à faire est d’éviter de construire un objet std::string dans le cas d’une conversion implicite à partir d’une chaîne terminée par un caractère nul:

 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.
 

14voto

n.caillou Points 696

std::string_view est fondamentalement juste un wrapper autour d'un const char*. Et en passant const char* signifie qu'il y aura un de moins pointeur dans le système en comparaison avec le passage const string* (ou const string&), parce qu' string* implique quelque chose comme:

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

Clairement, pour le but de transmettre const arguments, le premier pointeur est superflu.

p.s. Une communauté importante différence entre std::string_view et const char*, néanmoins, est que le string_views ne sont pas tenus d'être nul (ils ont construit en taille), et cela permet de hasard en place de l'épissage des chaînes plus longues.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X