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

SL & STL C++ Discussion :

Sur la rapidité des IOstream


Sujet :

SL & STL C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut Sur la rapidité des IOstream
    Citation Envoyé par 3DArchi Voir le message
    Toute la mécanique des streambuffer est en virtuelle. C'est d'ailleurs là que se passe le principale de la gestion du flux (hors formatage) :
    Et c'est une catastrophe...
    Vous permettez que je rage un peu sur IOStream ? Cette partie du C++ m'irrite systématiquement à chaque fois que je veux l'utiliser.

    <RAGE>
    Je suis tombé récemment sur un post sur stack overflow qui résume bien la situation :
    Q : C++0X Concepts are gone. Which other features should go too?

    A:
    Of course, plenty of old C cruft could be ditched too, but recently, I've discovered that the one change I'd really love to see is...... ditching the Iostreams library. Toss it out, and build a new STL-style I/O library based on generic programming.

    The current OOP-styled Iostreams library is ugly, slow, overcomplicated and inflexible. There's too much voodoo involved in defining new streams, too few standard stream types involved, too little flexibility (the problem that made me realize how limited the library is, was that I needed to extract a float from a string. Easy to do with stringstream, but if you need to do it often, you don't want to have to copy the input string every time (as the stringstream does) -- where's the stream that works on an existing iterator range? Or a raw array, even?)

    Throw IOstreams out, develop a modern replacement, and C++ will be vastly improved.
    Amen.

    Je ne sais pas si vous avez déjà lu cette série de post déprimante par Raymond Chen (guru mircrosoft C/C++) et Rico Mariani (guru microsoft C#) ?
    Elle commence par Raymond Chen qui écrit une petite appli C++ assez simple - un dictionnaire chinois/anglais. Rico Mariani décide alors de réécrire cette appli ligne par ligne en C#. Et Les perfs de l'appli C# enfonce celle de l'appli C++ dans les grandes largeurs !! (x10 !). Quelques jours plus tard et plusieurs update plus tard, l'appli C++ de Raymond Chen finit par rattraper les perfs du C# mais il a du se taper de nombreuses passes d'optimisation et écrire du code bas-niveau de plus en plus horrible et de plus en plus long.

    Et pourquoi l'appli C++ originale donne des perfs aussi mer.... ?
    IOstream

    L'horreur est la suivante : Pour peupler le dictionnaire, il faut ouvrir un fichier texte encodé au format 950 (du chinois) et le lire ligne par ligne tout en convertissant chaque ligne en Unicode.
    Le code C++ :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    std::wifstream src;
    src.imbue(std::locale(".950"));
    src.open("cedict.b5");
    wstring s;
    while (getline(src, s)) 
    {
       // do something
    }
    Le code C# :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    StreamReader src = new StreamReader(
                             "cedict.b5", 
                            System.Text.Encoding.GetEncoding(950));
    string s;
    while ((s = src.ReadLine()) != null)
    {
       // do something
    }
    Et bien c'est une boucherie.
    Ce code C# est x20 plus rapide chez moi. (VS2008)

    J'ai essayé d'analyser ce que fait respectivement chaque code et si je ne me suis pas trompé :

    Le code C# mappe le fichier en mémoire puis le parcours ligne par ligne, tout en convertissant chaque ligne en Unicode en appelant MultiByteToWideChar() sur la ligne entière.

    Le code C++ ouvre un flux, puis parcours le flux ligne par ligne et appelle pour chaque caractère la fonction de conversion vers l'Unicode codecvt::do_in, qui est bien évidement une fonction virtuelle qui ne s'inline pas, qui lock un mutex pour le fun, tout ça pour finalement appeler MultiByteToWideChar.... sur un seul caractère à la fois.
    </RAGE>

  2. #2
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Pour en rajouter une couche sur les iostream, il y a quelque chose que je n'ai jamais compris non plus :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (std::stringstream() << "toto" << 1 << "tutu").str()
    Ne compile pas, puisque operator<< sur un stringstream renvoie un ostream&...

    C++ a des type de retour covariants, pourquoi diable ne sont ils pas utilisés ?

  3. #3
    Membre Expert
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Par défaut
    je serais assez d'accord sur le fait que les iostreams sont un peu un rélent d'un passez sombre ou on ne pouvait pas faire bien mieux.

  4. #4
    Rédacteur
    Avatar de Laurent Gomila
    Profil pro
    Développeur informatique
    Inscrit en
    Avril 2003
    Messages
    10 651
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Par défaut
    Ne compile pas, puisque operator<< sur un stringstream renvoie un ostream&...

    C++ a des type de retour covariants, pourquoi diable ne sont ils pas utilisés ?
    Parce que operator << n'est pas une fonction virtuelle de ostream qui est surdéfinie dans les classes dérivées, tout simplement. Si je voulais récupérer un std::ostringstream& en retour d'un opérateur <<, il faudrait explicitement écrire cette version de l'opérateur avec ce type.

  5. #5
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Parce que operator << n'est pas une fonction virtuelle de ostream qui est surdéfinie dans les classes dérivées, tout simplement. Si je voulais récupérer un std::ostringstream& en retour d'un opérateur <<, il faudrait explicitement écrire cette version de l'opérateur avec ce type.
    J'étais persuadé qu'operator<< était virtuelle. Mais cela dit, cela ne change pas le fait qu'il serait possible et pratique de fournir cet opérateur.

    L'absence d'une méthode générale simple pour concaténer des objets sans passer par une variable intermédiaire est une des choses qui me manque ( et (static_cast<std::stringstream&>(std::stringstream() << a << b << c).str() ) manque cruellement d'élégance...

    Les variadics template apporteront une solution, c'est sûr.

  6. #6
    Rédacteur
    Avatar de Laurent Gomila
    Profil pro
    Développeur informatique
    Inscrit en
    Avril 2003
    Messages
    10 651
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Par défaut
    J'étais persuadé qu'operator<< était virtuelle
    En fait même si c'était le cas, la chaîne serait tout de même cassée lorsqu'on arriverait sur un opérateur << pour un type perso qu'on a écrit nous-même, et qui renverrait un std::ostream&.

    Mais cela dit, cela ne change pas le fait qu'il serait possible et pratique de fournir cet opérateur.
    Ca fait pas mal de boulot, d'écrire la même fonction avec tous les types dérivés possibles d'ostream

    L'absence d'une méthode générale simple pour concaténer des objets sans passer par une variable intermédiaire est une des choses qui me manque
    Ca manque en effet, mais ça s'implémente facilement. Comme tu le dis, le prochain standard devrait permettre d'avoir des trucs plus sympas à ce niveau.

  7. #7
    Expert confirmé

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Par défaut
    Citation Envoyé par Joel F Voir le message
    je serais assez d'accord sur le fait que les iostreams sont un peu un rélent d'un passez sombre ou on ne pouvait pas faire bien mieux.
    Je parie que meme en ayant vu les IOStream, la plupart des gens ne feraient pas mieux maintenant.

    Les locales -- et elles ont des interactions avec les IOStreams -- sont a mon avis bien plus contestables. Pour commencer, elles sont a moitie incomprehensibles, ensuite elles ont beaucoup moins bien vieilli.

  8. #8
    Membre Expert
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Par défaut
    Citation Envoyé par Jean-Marc.Bourguet Voir le message
    Je parie que meme en ayant vu les IOStream, la plupart des gens ne feraient pas mieux maintenant.

    Les locales -- et elles ont des interactions avec les IOStreams -- sont a mon avis bien plus contestables. Pour commencer, elles sont a moitie incomprehensibles, ensuite elles ont beaucoup moins bien vieilli.
    j'ai jamais dit le contraire
    et je suis relativzment tout a fait d'accord sur les locales :€

  9. #9
    Expert confirmé

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Par défaut
    Citation Envoyé par Arzar Voir le message
    Et c'est une catastrophe...
    Vous permettez que je rage un peu sur IOStream ?
    Je ne vois pas trop le rapport avec le sujet. Tu compares me semble-t'il deux conception tres OO des IO.

    J'ai essayé d'analyser ce que fait respectivement chaque code et si je ne me suis pas trompé :

    Le code C# mappe le fichier en mémoire puis le parcours ligne par ligne, tout en convertissant chaque ligne en Unicode en appelant MultiByteToWideChar() sur la ligne entière.

    Le code C++ ouvre un flux, puis parcours le flux ligne par ligne et appelle pour chaque caractère la fonction de conversion vers l'Unicode codecvt::do_in, qui est bien évidement une fonction virtuelle qui ne s'inline pas, qui lock un mutex pour le fun, tout ça pour finalement appeler MultiByteToWideChar.... sur un seul caractère à la fois.
    </RAGE>
    Qu'est-ce qui empeche l'implementation des IOStreams en C++ de faire la meme chose que l'implementation C#: rien. Le probleme que tu souleves c'est pas du aux IOStreams mais a l'implementation qui n'est pas, sur ce point, de la meme qualite que l'implementation C#.

  10. #10
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Citation Envoyé par Jean-Marc.Bourguet Voir le message
    Qu'est-ce qui empeche l'implementation des IOStreams en C++ de faire la meme chose que l'implementation C#: rien. Le probleme que tu souleves c'est pas du aux IOStreams mais a l'implementation qui n'est pas, sur ce point, de la meme qualite que l'implementation C#.
    Je ne suis pas totalement d'accord avec ton "rien". C# définit la locale une fois pour tout le fichier. C++ permet de changer la locale à tout moment sur le flux.

    Cette différence d'approche fait que C# peut faire facilement la conversion sur tout son buffer de lecture, là où une implémentation C++ ne peut pas faire cette optimisation (ou, au prix d'une beaucoup plus grande complexité d'implémentation) ==> une implémentation naïve fera la conversion au moment où la valeur est récupérée par l'utilisateur, pas au moment où elle est lue. Une implémentation qui voudrait faire mieux prend le risque de faire des centaines de conversions inutiles dans le cas défavorable d'un fichier multi-locales.

    Changer la locale au cours du parcours d'un fichier peut effectivement avoir un sens, dans le cas d'une déclaration xml ou d'une BOM. Mais c'est quelque chose qui ne sera effectué qu'une fois, et généralement on passera de "locale courante", donc pas de conversion à effectuer sur le flux, à "locale définie par le fichier" --> seule cette opération me semble nécessaire pour une librairie de flux standards, et ceci s'optimise beaucoup plus facilement que pouvoir changer à tout moment la locale.

  11. #11
    Expert confirmé

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Je ne suis pas totalement d'accord avec ton "rien". C# définit la locale une fois pour tout le fichier. C++ permet de changer la locale à tout moment sur le flux.

    Cette différence d'approche fait que C# peut faire facilement la conversion sur tout son buffer de lecture, là où une implémentation C++ ne peut pas faire cette optimisation (ou, au prix d'une beaucoup plus grande complexité d'implémentation).
    Tu me permettras de ne pas etre d'accord avec la complexite beaucoup plus grande. imbue dans l'implementation GNU fait une cinquante de lignes, dont au moins une quarantaine serait la meme si cette optimisation n'etait pas faite; il y a environ 5 lignes dans underflow qui traitent aussi ce cas. Ca fait au maximum 15 lignes ajoutees pour traiter le cas du changement de code pendant la lecture.

    Une implémentation qui voudrait faire mieux prend le risque de faire des centaines de conversions inutiles dans le cas défavorable d'un fichier multi-locales
    Si le fait de faire la conversion par bloc plutot que par caractere te fait gagner un x10 (j'ai pas ete voir si GNU mappait ou pas le fichier, l'optimisation sur la conversion est independante de cela), il faut souvent changer de codage pour que l'optimisation ne soit pas un gain net au final. (Sans compter que pour que ca coute il faut que la version encodee soit valable pour l'ancien code aussi).

  12. #12
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Citation Envoyé par Jean-Marc.Bourguet Voir le message
    Tu me permettras de ne pas etre d'accord avec la complexite beaucoup plus grande. imbue dans l'implementation GNU fait une cinquante de lignes, dont au moins une quarantaine serait la meme si cette optimisation n'etait pas faite; il y a environ 5 lignes dans underflow qui traitent aussi ce cas. Ca fait au maximum 15 lignes ajoutees pour traiter le cas du changement de code pendant la lecture.
    Oui, j'ai exagéré dans mon propos. Ce n'est pas non plus insurmontable, loin de là.

    Si le fait de faire la conversion par bloc plutot que par caractere te fait gagner un x10 (j'ai pas ete voir si GNU mappait ou pas le fichier, l'optimisation sur la conversion est independante de cela), il faut souvent changer de codage pour que l'optimisation ne soit pas un gain net au final. (Sans compter que pour que ca coute il faut que la version encodee soit valable pour l'ancien code aussi).
    Je ne suis pas sûr de comprendre ce que tu veux dire par là.

    Sinon, j'ai fait un test rapide, entre g++ et mono. g++ est environ deux fois plus rapide que mono pour la lecture d'un fichier utf-8 et conversion en wide char, ce qui confirme tes dires (mauvaise implémentation chez microsoft ?).

  13. #13
    Expert confirmé

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Oui, j'ai exagéré dans mon propos. Ce n'est pas non plus insurmontable, loin de là.
    On gagne tellement sur les lectures normales qu'il faudrait changer de locale tres souvent pour que ce qu'on perd lors de ce changement ne soit pas compense par ce qui est gagne lors des lecture.

    Sinon, j'ai fait un test rapide, entre g++ et mono. g++ est environ deux fois plus rapide que mono pour la lecture d'un fichier utf-8 et conversion en wide char, ce qui confirme tes dires (mauvaise implémentation chez microsoft ?).
    Mono n'est pas de MS que je sache (ou bien tu es sur qu'il a des perf comparable a l'implementation de MS?).

  14. #14
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Intéressant
    Je n'étais pas du tout au courant pour ceci :
    Citation Envoyé par white_tentacle Voir le message
    Je ne suis pas totalement d'accord avec ton "rien". C# définit la locale une fois pour tout le fichier. C++ permet de changer la locale à tout moment sur le flux.

    Cette différence d'approche fait que C# peut faire facilement la conversion sur tout son buffer de lecture
    Effectivement, j'ai regardé en détail la manière de faire du getline version C# avec MSVC10 et le reflector et ça se passe comme ceci :

    Lors du premier getline, un buffer de 1024 octets est lu par la fonction win32 "native" ReadFile, et ce buffer est aussitôt entièrement convertit en unicode.

    Les getline suivants ne lance donc en fait pas de nouvelle lecture sur le disque, ils se contentent de trouver le prochain \n dans ce buffer et de renvoyer une copie de la ligne. Dès qu'on dépasse les 1024 octets, une lecture avec ReadFile est relancé, etc.

    Pour la STL de MSVC10, j'ai *tenté* de regardé plus en détails, mais le code C++ est devenu si compliqué que je ne suis pas encore certain d'avoir tout compris .

Discussions similaires

  1. [JONAS][EJB]erreur sur la construction des EJB
    Par silvermoon dans le forum Eclipse Java
    Réponses: 2
    Dernier message: 04/06/2004, 18h53
  2. Un peu de lumière sur l'arborescence des fichiers de Linux
    Par Noki dans le forum Administration système
    Réponses: 6
    Dernier message: 07/04/2004, 16h16
  3. question sur le format des images ..
    Par vbcasimir dans le forum Langages de programmation
    Réponses: 7
    Dernier message: 28/08/2003, 12h08
  4. Probleme sur le Fields des fichiers Xmlgram
    Par Sandrine75 dans le forum XMLRAD
    Réponses: 4
    Dernier message: 20/03/2003, 17h09

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