std::string_view
l'a fait en C ++ 17 et il est largement recommandé de l'utiliser à la place de const std::string&
.
L'une des raisons est la performance.
Quelqu'un peut-il expliquer comment est / sera exactement std::string_view
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)
c++
string
c++17
string-view
Patryk
la source
la source
std::string_view
est juste une abstraction de la paire (char * begin, char * end). Vous l'utilisez lorsque vous créez unestd::string
copie inutile.std::string
(string_view peut accepter que des tableaux bruts, des vecteurs,std::basic_string<>
avec allocateurs non par défaut , etc. , etc. , etc. Oh, et d' autres string_views évidemment)Réponses:
std::string_view
est plus rapide dans quelques cas.Tout d'abord,
std::string const&
nécessite que les données soient dans unstd::string
tableau C, et non dans un tableau C brut,char const*
renvoyé par une API C,std::vector<char>
produit par un moteur de désérialisation, etc. La conversion de format évitée évite de copier des octets, et (si la chaîne est plus longue que le SBO¹ pour l'std::string
implémentation particulière ) évite une allocation de mémoire.Aucune allocation n'est effectuée dans le
string_view
cas, mais il y aurait sifoo
pris unstd::string const&
au lieu d'unstring_view
.La deuxième raison très importante est qu'elle permet de travailler avec des sous-chaînes sans copie. Supposons que vous analysez une chaîne json de 2 gigaoctets (!) ². Si vous l'analysez
std::string
, chacun de ces nœuds d'analyse où ils stockent le nom ou la valeur d'un nœud copie les données d'origine de la chaîne de 2 Go vers un nœud local.Au lieu de cela, si vous l'analysez en
std::string_view
s, les nœuds se réfèrent aux données d'origine. Cela peut économiser des millions d'allocations et réduire de moitié les besoins en mémoire lors de l'analyse.L'accélération que vous pouvez obtenir est tout simplement ridicule.
C'est un cas extrême, mais d'autres cas «obtenez une sous-chaîne et travaillez avec elle» peuvent également générer des accélérations décentes avec
string_view
.Une partie importante de la décision est ce que vous perdez en utilisant
std::string_view
. Ce n'est pas grand-chose, mais c'est quelque chose.Vous perdez la terminaison nulle implicite, et c'est à peu près tout. Donc, si la même chaîne est passée à 3 fonctions qui nécessitent toutes un terminateur nul, la conversion en
std::string
une fois peut être judicieuse. Ainsi, si votre code est connu pour avoir besoin d'un terminateur nul et que vous ne vous attendez pas à ce que des chaînes alimentées à partir de tampons de type C ou similaires, prenez peut-être unstd::string const&
. Sinon, prenez unstd::string_view
.Si
std::string_view
un indicateur indiquait s'il était terminé (ou quelque chose de plus sophistiqué), il supprimerait même cette dernière raison d'utiliser astd::string const&
.Il y a un cas où prendre un
std::string
avec nonconst&
est optimal par rapport à astd::string_view
. Si vous devez posséder une copie de la chaîne indéfiniment après l'appel, la prise de valeur est efficace. Vous serez soit dans le cas SBO (et pas d'allocations, juste quelques copies de caractères pour le dupliquer), ou vous pourrez déplacer le tampon alloué par tas dans un localstd::string
. Avoir deux surchargesstd::string&&
etstd::string_view
peut être plus rapide, mais seulement de manière marginale, et cela entraînerait une légère surcharge de code (ce qui pourrait vous coûter tous les gains de vitesse).¹ Optimisation des petits tampons
² Cas d'utilisation réel.
la source
Une façon dont string_view améliore les performances est qu'il permet de supprimer facilement les préfixes et suffixes. Sous le capot, string_view peut simplement ajouter la taille du préfixe à un pointeur dans un tampon de chaîne, ou soustraire la taille du suffixe du compteur d'octets, c'est généralement rapide. std :: string d'autre part doit copier ses octets lorsque vous faites quelque chose comme substr (de cette façon, vous obtenez une nouvelle chaîne qui possède son tampon, mais dans de nombreux cas, vous voulez simplement obtenir une partie de la chaîne d'origine sans copier). Exemple:
Avec std :: string_view:
Mettre à jour:
J'ai écrit un point de repère très simple pour ajouter des nombres réels. J'ai utilisé la bibliothèque de référence Google génial . Les fonctions référencées sont:
Résultats
(Linux x86_64, gcc 6.2, "
-O3 -DNDEBUG
"):la source
Il y a 2 raisons principales:
string_view
est une tranche dans un tampon existant, elle ne nécessite pas d'allocation de mémoirestring_view
est passé par valeur, pas par référenceLes avantages d'avoir une tranche sont multiples:
char const*
ouchar[]
sans allouer un nouveau tamponDes performances meilleures et plus cohérentes partout.
Le passage par valeur présente également des avantages par rapport au passage par référence, car 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ée. Par conséquent, le compilateur doit récupérer à nouveau le contenu de la chaîne après chaque appel dans une méthode opaque (pointeur sur les données, longueur, ...).D'un autre côté, lors du passage d'une
string_view
valeur par, le compilateur peut déterminer statiquement qu'aucun autre code ne peut modifier la longueur et les pointeurs de données maintenant sur la pile (ou dans les registres). Par conséquent, il peut les "mettre en cache" sur les appels de fonction.la source
Une chose qu'il peut faire est d'éviter de construire un
std::string
objet dans le cas d'une conversion implicite à partir d'une chaîne terminée par null:la source
const std::string str{"goodbye!"}; foo(str);
probablement pas plus rapide avec string_view qu'avec string &string_view
sera pas lent car il doit copier deux pointeurs au lieu d'un pointeurconst string&
?std::string_view
est fondamentalement juste un wrapper autour d'unconst char*
. Et passerconst char*
signifie qu'il y aura un pointeur de moins dans le système par rapport à passerconst string*
(ouconst string&
), car celastring*
implique quelque chose comme:De toute évidence, dans le but de passer des arguments const, le premier pointeur est superflu.
ps Une différence substantielle entre
std::string_view
etconst char*
, néanmoins, est que les string_views ne doivent pas être terminées par un caractère nul (elles ont une taille intégrée), et cela permet un épissage aléatoire sur place de chaînes plus longues.la source
std::string_view
s sont juste fantaisistesconst char*
, point final. GCC les implémente comme ceci:class basic_string_view {const _CharT* _M_str; size_t _M_len;}
std::string const*
. Et ce diagramme est inintelligible. @ n.caillou: Votre propre commentaire est déjà plus précis que la réponse. Cela faitstring_view
plus que "fantaisiechar const*
" - c'est vraiment assez évident.std::string const*
etstd::string const&
êtes les mêmes, n'est-ce pas?