2008-12-12 10 views
36

J'ai une situation où je dois traiter de gros (beaucoup de GB) quantités de données en tant que tel:initialisation std :: string de char * sans copie

  1. construire une grande chaîne en ajoutant beaucoup plus petit (C de type char *) les chaînes
  2. couper la chaîne
  3. convertir la chaîne en C++ const std :: string pour le traitement (lecture seule)
  4. répétition

da ta dans chaque itération sont indépendants. Ma question est, je voudrais minimiser (si possible éliminer) l'utilisation de la mémoire allouée par tas, comme c'est actuellement mon plus grand problème de performance. Y a-t-il un moyen de convertir une chaîne C (char *) en une chaîne stl C++ (std :: string) sans avoir besoin de std :: string pour allouer/copier les données en interne?

Sinon, pourrais-je utiliser des chaînes de caractères ou quelque chose de similaire pour réutiliser un grand tampon?

Edit: Merci pour les réponses, pour plus de clarté, je pense une question révisée serait:

Comment puis-je construire (via plusieurs ajouter ses) un stl C++ chaîne efficace. Et si vous effectuez cette action dans une boucle, où chaque boucle est totalement indépendante, comment puis-je réutiliser cet espace alloué.

Répondre

17

Est-il possible d'utiliser une chaîne C++ à l'étape 1? Si vous utilisez string::reserve(size_t), vous pouvez allouer un tampon suffisamment grand pour empêcher l'allocation de plusieurs tas tout en ajoutant les chaînes les plus petites, puis vous pouvez simplement utiliser cette même chaîne C++ dans toutes les étapes restantes.

Voir this link pour plus d'informations sur la fonction reserve.

+0

Votre solution ressemble à ce qui suit, bien que, quand vous dites "vous pouvez simplement utiliser cette même chaîne C++", voulez-vous dire utiliser clear(), et continuer à construire la chaîne suivante? – Akusete

+0

Cette solution est la plus simple pour moi d'adopter, actuellement, bien que je ne sache pas si la réutilisation est réellement spécifiée par la norme (bien que cela semble fonctionner sur mon implémentation) – Akusete

+0

L'utilisation de clear() devrait fonctionner.Pour autant que je sache, clear() ne libère pas la mémoire utilisée par la chaîne, et n'affecte donc pas l'espace alloué par reserve(). –

1

Chaque itération est-elle suffisamment indépendante pour que vous puissiez utiliser la même chaîne std :: string à chaque itération? On peut espérer que votre implémentation de std :: string est assez intelligente pour réutiliser la mémoire si vous lui attribuez un const char * quand il était précédemment utilisé pour quelque chose d'autre.

L'affectation d'un caractère * dans une chaîne std :: doit toujours au moins copier les données. La gestion de la mémoire est l'une des principales raisons d'utiliser std :: string, donc vous ne serez pas en mesure de le surcharger.

7

Pour aider avec de très grandes chaînes SGI a la classe Rope dans son STL.
Non standard mais peut être utile.

http://www.sgi.com/tech/stl/Rope.html

Apparemment, la corde est dans la prochaine version de la norme :-)
Notez la blague des développeurs. Une corde est une grosse corde. (Ha Ha) :-)

18

Vous ne pouvez pas réellement créer une chaîne std :: sans copier les données. Une chaîne de caractères réutiliserait probablement la mémoire d'un passage à l'autre (bien que je pense que la norme soit muette sur le fait de savoir si elle doit réellement l'être), mais cela n'empêcherait pas la copie.

Une approche commune à ce genre de problème consiste à écrire le code qui traite les données à l'étape 3 pour utiliser une paire d'itérateur début/fin; alors il peut facilement traiter soit un std :: string, un vecteur de caractères, une paire de pointeurs bruts, etc. Contrairement à un type de conteneur comme std :: string, il ne connaîtrait plus ou ne se soucierait plus comment la mémoire était allouée, car il appartiendrait toujours à l'appelant. Portant cette idée à sa conclusion logique est boost::range, qui ajoute tous les constructeurs surchargés pour toujours laisser l'appelant juste passer une chaîne/vector/list/tout type de conteneur avec .begin() et .end(), ou séparer les itérateurs.Après avoir écrit votre code de traitement pour travailler sur une plage d'itérateur arbitraire, vous pouvez même écrire un itérateur personnalisé (pas aussi dur que ça en a l'air, juste un objet avec quelques typedefs standard, et l'opérateur ++/*/=/== /! = surchargé pour obtenir un itérateur avant seulement) qui prend soin d'avancer vers le fragment suivant chaque fois qu'il touche la fin de celui sur lequel il travaille, en sautant par-dessus les espaces (je suppose que c'est ce que vous vouliez dire par trim) . Que vous n'avez jamais eu à assembler la chaîne entière de façon contiguë. Que ce soit ou non une victoire dépend du nombre de fragments/combien de fragments que vous avez. C'est essentiellement ce que la corde SGI mentionnée par Martin York est: une chaîne où append forme une liste chaînée de fragments au lieu d'un tampon contigu, ce qui convient donc à des valeurs beaucoup plus longues.


MISE À JOUR (depuis sur cette réponse que je vois encore upvotes occasionnels):

17 C++ introduit un autre choix: std::string_view, qui a remplacé std :: string dans de nombreuses signatures de fonction, est un non -notation de référence à une donnée de caractère. Il est implicitement convertible à partir de std :: string, mais peut aussi être explicitement construit à partir de données contiguës détenues ailleurs, évitant ainsi la copie inutile de std :: string imposes.

+0

Je pense que votre solution est la meilleure approche (chaning le code de traitement) malheureusement dans cette situation ce n'est pas une option. – Akusete

+1

Existe-t-il une méthode standard spécifiée pour la réutilisation du tampon? Je ne veux tout simplement pas compter sur l'implémentation sur une plate-forme spécifique. – Akusete

+0

À moins que ce code de traitement ne soit une fonction de bibliothèque qui n'utilise pas d'itérateurs ni de chaînes, il s'agit simplement d'une ancienne taille 'char *' +. – SasQ

0

Dans ce cas, pourrait-il être préférable de traiter le char * directement, au lieu de l'assigner à une chaîne std :: string.

+3

Oui, bien que les entrées (C char * 's) et la sortie (std :: string) ne sont pas sous mon contrôle. – Akusete

4

Ceci est une réponse de réflexion latérale, ne pas aborder directement la question, mais "penser" autour d'elle. Peut-être utile, peut-être pas ...

Le traitement en lecture seule de std :: string ne nécessite pas vraiment un sous-ensemble très complexe des fonctionnalités de std :: string. Est-il possible que vous puissiez faire une recherche/remplacer sur le code qui effectue tout le traitement sur std :: strings donc il prend un autre type à la place? Commencez avec une classe vide:

classe structure_ligne {};

Remplacez ensuite toutes les références std :: string par light_string. Effectuez une compilation pour savoir exactement quelles opérations sont nécessaires sur light_string pour qu'il remplace le remplacement. Ensuite, vous pouvez faire en sorte que votre implémentation fonctionne comme vous le souhaitez.