IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

A propos de std::streambuf


Sujet :

C++

  1. #1
    Membre confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2012
    Messages
    56
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Distribution

    Informations forums :
    Inscription : Juin 2012
    Messages : 56
    Par défaut A propos de std::streambuf
    Bonsoir,

    j'ai un problème avec l'utilisation de std::streambuf. Apparemment, il travaille avec des char*, mais visiblement, j'ai du mal à gérer la mémoire dans mon projet.

    Admettons le code suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    template <typename T>
    inline std::string read(std::ifstream& ifs, const T& offset, const T& size)
    {
    	std::streambuf *pbuf = ifs.rdbuf();
    	char* contents = new char[size + 1];
    	pbuf->pubseekoff(offset, ifs.beg);
    	pbuf->sgetn(contents, size);
    	contents[size] = '\0';
    	std::string datas(contents);
    	delete[] contents;
    	delete pbuf;
    	return datas;
    }
    Cette fonction est appelée des millions de fois , la désallocation ne se semble pas se dérouler correctement, ma mémoire RAM finit par saturer.. J'ai du oublier un détail.

    Merci d'avance pour vos réponses.

  2. #2
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 751
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 751
    Par défaut
    Je ne suis pas expert de std::streambuf mais essayes d'éviter les allocations et les desallocations.

    Pour
    • La variable contents passe là en static ou en global et fixe sa taille avec la plus grande taille possible (tu dois la connaître)
    • La variable pbuf là c'est inévitable, à moins d'avoir une seule variable ifs (puisqu'on prend son pointeur sur son objet interne)
    • Pour ta valeur de retour, il me semble qu'un const std::string& évite des recopies inutiles. Mais je pense qu'un const est trop limitant.


    Par contre, il semble te manquer un ifs.close(); juste avant la ligne 10

  3. #3
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    752
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 752
    Par défaut
    C'est dommage que l'application ne plante pas (elle devrait). Mais un outil d'analyse (asan, valgrind, ...) aurait probablement relevé l'erreur.
    La ligne 11 est de trop. Le pbuf est détruit et ifs n'en est même pas informé !

    Tu peux aussi te passer complètement du buffer intermédiaire.
    Depuis C++11, la mémoire d'un std::string est garantit continue, ce qui permet de remplacer content par &datas[0] (après application de std::string::resize).
    Par contre, il y a toujours le memset dû au resize.

    J'ai une fonction de lecture de fichier un peu sioux. Comme aucune donnée n'est présente dans le buffer du streambuf, tout le contenu est directement écrit dans le buffer donné à sgetn (pas d'intermédiaire). Remplacer le buffer interne du streambuf permet aussi de l'empêcher d'en allouer un.

    @foetus: pourquoi vouloir fermer le fichier ? La fonction ne s'appelle pas read_and_close

  4. #4
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 751
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 751
    Par défaut
    Citation Envoyé par jo_link_noir Voir le message
    @foetus: pourquoi vouloir fermer le fichier ? La fonction ne s'appelle pas read_and_close
    En regardant les exemples sur cplusplus.com ici (qui est très proche du code de bowow) et

  5. #5
    Membre confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2012
    Messages
    56
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Distribution

    Informations forums :
    Inscription : Juin 2012
    Messages : 56
    Par défaut
    foetus, contrairement à ce que tu peux penser, je ne peux pas prédire la taille du buffer à l'avance, les données sont très très denses, je ne peux donc pas les lire, d'ou mon allocation dynamique. Comme cette fonction est appelée des milliards de fois, le fichier reste ouvert jusqu'à la lecture complète du fichier à partir des offsets sauvegardés dans un vecteur de structure.

    jo_link_noir, merci bien, comme toujours! Bon j'ai remplacé mon code par celui-ci:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template <typename T>
    std::string read(std::ifstream& ifs, const T& offset, const T& size)
    {
    	std::string datas;
    	datas.resize(size);
    	std::streambuf *pbuf = ifs.rdbuf();
    	pbuf->pubseekoff(offset, ifs.beg);
    	pbuf->sgetn(&datas[0], size);
     
    	return datas;
    }
    je récupère bien mes chaines de caractères et la RAM me dit merci.

    Par contre, je recherche à supprimer des '\n' en plein milieu de la chaine de caractères, existe-t-il un méthode adaptée pour ça? j'avais pensé faire une fonction un peu dérivée de celle du dessus:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    template <typename T>
    std::string read_char(std::ifstream& ifs, const T& offset, const T& size)
    {
    	std::string datas;
    	std::streambuf *pbuf = ifs.rdbuf();
    	pbuf->pubseekoff(offset, ifs.beg);
    	do
    	{
    		char c = pbuf->sgetc();
    		if (c != '\n')
    		  datas.push_back(c);
     
    	} while (pbuf->snextc() != '>');
     
    	return datas;
    }

  6. #6
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    752
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 752
    Par défaut
    Citation Envoyé par bowow Voir le message
    Par contre, je recherche à supprimer des '\n' en plein milieu de la chaine de caractères, existe-t-il un méthode adaptée pour ça?
    Pas à ma connaissance. Si les sauts de lignes sont peu nombreux, peut-être une boucle sur sgetn() et std::remove(..., '\n') ?

    Attention avec read_char. Avec resize, il ne faut pas insérer en fin, mais modifier les valeurs du tableaux (ou utiliser std::string::reserve + insert/push_back).
    La ligne 15 n'a pas d'effet en l'état: resize() joue sur la valeur de size(). Si on lui donne la valeur de size(), alors il n'y a rien à faire.
    Concernant la boucle, je ne comprends pas ce que vient faire la comparaison sur '>' ? Un parser html ?

    Aussi, tu ne fais pas de vérification d'erreur de lecture ?

  7. #7
    Membre confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2012
    Messages
    56
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Distribution

    Informations forums :
    Inscription : Juin 2012
    Messages : 56
    Par défaut
    Ouais en fait, j'avais posté le code à 3h du mat', sans vraiment tester, alors qu'un simple push_back est plus adapté et redimensionne automatiquement. J'ai réédité mon poste initial.

    je testerais bien ta méthode avec sgetn et std::remove, pour voir et comparer. (je bench pas mal, je recherche toujours les moindres petites optimisations).

    Pour les erreurs de lecture, je ne vois pas trop ou tu veux en venir, vérifier que c != null ?

  8. #8
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    752
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 752
    Par défaut
    Citation Envoyé par bowow Voir le message
    Pour les erreurs de lecture, je ne vois pas trop ou tu veux en venir, vérifier que c != null ?
    Vérifier par exemple qu'il n'y a plus rien à lire ou qu'il n'est plus possible de lire (il n'y a pas de distinction entre la fin de fichier et une erreur, il faut regarder errno pour ça).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    using traits = stream_buf::traits_type;
    const auto eof = traits::eof();
    auto c = sb->sgetc();
     
    while (!traits::eq_int_type(c, eof)) {
        ...
        c = sb->snextc();
    }

    Comme j'y pense, que devient la chaîne retournée par read et read_char ? Si elle peut être réutilisée, il est préférable de la fournir en référence aux fonctions pour ne pas allouer systématiquement la mémoire quand resize/reserve est appelé.

  9. #9
    Membre confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2012
    Messages
    56
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Distribution

    Informations forums :
    Inscription : Juin 2012
    Messages : 56
    Par défaut
    Comme j'y pense, que devient la chaîne retournée par read et read_char ? Si elle peut être réutilisée, il est préférable de la fournir en référence aux fonctions pour ne pas allouer systématiquement la mémoire quand resize/reserve est appelé.
    A chaque itération, les offsets changent et la chaîne retournée est toujours différente. Par contre, dans certains cas, la taille peut stagner sur plusieurs itérations, mais impossible à prédire.

    Mais bon les performances de mon code ne sont pas terrible, au niveau du déplacement dans le fichier.

    comment réagit pubseekoff ? Si je lui dis tu te déplaces à l'offset 1.000.000, il se déplace immédiatement à cette position, ou il est obligé de déplacer l'offset char par char ?
    Actuellement je sauvegarde l'offset depuis le début du fichier, puis je démarre à std::ios_base::beg à chaque fois.
    S'il se déplace char par char, autant calculer l'offset à partir de l'offset précédent et utiliser std::ios_base::cur ?

  10. #10
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    752
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 752
    Par défaut
    Citation Envoyé par bowow Voir le message
    A chaque itération, les offsets changent et la chaîne retournée est toujours différente. Par contre, dans certains cas, la taille peut stagner sur plusieurs itérations, mais impossible à prédire.
    Je vais donner un exemple pour mieux me faire comprendre.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    std::string s;
    while (!(s = read(...).empty()) {
      std::cout << s;
    }
    Dans cette exemple, read fait systématiquement une allocation alors que si s était passé en paramètre, il n'y en aurait uniquement quand la taille demandée est plus grande que la capacité de la chaîne.

    Citation Envoyé par bowow Voir le message
    Mais bon les performances de mon code ne sont pas terrible, au niveau du déplacement dans le fichier. comment réagit pubseekoff ?

    Si je lui dis tu te déplaces à l'offset 1.000.000, il se déplace immédiatement à cette position, ou il est obligé de déplacer l'offset char par char ?
    Heureusement que nom. Derrière se cache un appel à lseek, qui fait appel au driver disque pour déplacer le pointeur. À moins de travailler sur des périphériques particuliers, le saut est "immédiat". Mais le disque n'est pas réputé rapide ; comme toutes les io en fait.

    Je ne sais pas si c'est possible ou s'il y aura un véritable impact, mais, peux-tu lire par offset croissant ?
    C'est peut-être même plus rapide de lire par gros morceau dans un buffer et travailler un maximum avec celui-ci ? Voir carrément avec le buffer interne de streambuf (streambuf::showmanyc et streambuf::pubsetbuf)
    Je n'ai jamais testé, mais c'est peut-être plus efficace avec std::ios::cur ?

    Il faudrait plus d'infos.

  11. #11
    Membre confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2012
    Messages
    56
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Distribution

    Informations forums :
    Inscription : Juin 2012
    Messages : 56
    Par défaut
    En fait désolé, je me suis précipité dans ces affirmations hasardeuses, en comparant avec mon ancienne solution, la recherche de motif est beaucoup plus rapide car elle ne m'oblige plus à tout relire le fichier à chaque fois, mais à pointer sur les offsets, donc on y gagne en faisant les sauts d'offsets. Tu n'as fait que confirmer mon point de vue à propos de pubseekof, le contraire aurait été étonnant, donc ouf je garde mon algorithm actuel.

    Par contre, ton optimisation est plutôt bonne à prendre et non négligeable, surtout si la taille des données est importante.

    Je ne sais pas si c'est possible ou s'il y aura un véritable impact, mais, peux-tu lire par offset croissant ?
    Les offsets sont déjà enregistrés en mémoire par ordre croissant.

    C'est peut-être même plus rapide de lire par gros morceau dans un buffer et travailler un maximum avec celui-ci ? Voir carrément avec le buffer interne de streambuf (streambuf::showmanyc et streambuf::pubsetbuf)
    Cette approche pourrait être intéressante à exploiter.

    Je n'ai jamais testé, mais c'est peut-être plus efficace avec std::ios::cur ?
    Aucune idée, mais ça m'obligerait à faire beaucoup de changements dans les algorithmes et pas sur que le bénéfice soit flagrant.

Discussions similaires

  1. A propos de std::istreambuf_iterator<>
    Par bowow dans le forum C++
    Réponses: 2
    Dernier message: 29/04/2015, 00h15
  2. Réponses: 18
    Dernier message: 19/08/2011, 16h21
  3. question à propos d'un std::vector encapsuler
    Par Date90 dans le forum C++
    Réponses: 2
    Dernier message: 05/04/2009, 15h20
  4. A propos de std::ostrstream
    Par camboui dans le forum SL & STL
    Réponses: 20
    Dernier message: 08/01/2009, 18h21
  5. A propos du composant DBGrid
    Par _Rico_ dans le forum C++Builder
    Réponses: 2
    Dernier message: 24/07/2002, 09h18

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo