Pourquoi istream / ostream est-il lent

À 50: 40 de http://channel9.msdn.com/Events/GoingNative/2013/Writing-Quick-Code-in-Cpp-Quickly Andrei Alexandrescu fait une blague sur la façon dont istream n'est pas efficace/lent.

J'ai eu un problème dans le passé avec ostream étant lent et fwrite étant significativement plus rapide (réduisant de nombreuses secondes lors de l'exécution de la boucle principale une fois) mais je n'ai jamais compris pourquoi ni regardé dedans.

Qu'est-ce qui rend istream et ostream lents en C++? ou au moins lent par rapport à d'autres choses (comme fread / fget, fwrite) qui satisferait également les besoins.

30
demandé sur gd1 2013-09-09 01:21:45

4 réponses

En fait, IOStreams n'a pas à être lent! Il s'agit de les mettre en œuvre d'une manière raisonnable pour les rendre rapides, cependant. La plupart des bibliothèques C++ standard ne semblent pas accorder trop d'attention à la mise en œuvre D'IOStreams. Il y a longtemps, quand mon CXXRT était toujours maintenu, il était à peu près aussi rapide que stdio - lorsqu'il était utilisé correctement!

Notez qu'il y a peu de pièges de performance pour les utilisateurs disposés avec IOStreams, cependant. Les directives suivantes s'appliquent à tous les IOStream implémentations mais surtout à celles qui sont adaptées pour être rapides:

  1. Lors de l'utilisation de std::cin, std::cout, etc. vous devez appeler std::sync_with_stdio(false)! Sans cet appel, toute utilisation des objets de flux standard est nécessaire pour synchroniser avec les flux standard de C. Bien sûr, lors de l'utilisation de std::sync_with_stdio(false), il est supposé que vous ne mélangez pas std::cin avec stdin, std::cout avec stdout, etc.
  2. Do pas utiliser std::endl car il des mandats bien des vidages de mémoire tampon. De même, Ne définissez pas std::ios_base::unitbuf ou utilisez std::flush inutilement.
  3. lorsque vous créez vos propres tampons de flux (OK, peu d'utilisateurs le font), assurez-vous qu'ils utilisent un tampon interne! Le traitement des caractères individuels passe par plusieurs conditions et une fonction virtual qui le rend hideusement lent.
35
répondu Dietmar Kühl 2015-07-25 16:06:46

Peut-être que cela peut donner une idée de ce à quoi vous avez affaire:

#include <stdio.h>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <fstream>
#include <time.h>
#include <string>
#include <algorithm>

unsigned count1(FILE *infile, char c) { 
    int ch;
    unsigned count = 0;

    while (EOF != (ch=getc(infile)))
        if (ch == c)
            ++count;
    return count;
}

unsigned int count2(FILE *infile, char c) { 
    static char buffer[8192];
    int size;
    unsigned int count = 0;

    while (0 < (size = fread(buffer, 1, sizeof(buffer), infile)))
        for (int i=0; i<size; i++)
            if (buffer[i] == c)
                ++count;
    return count;
}

unsigned count3(std::istream &infile, char c) {    
    return std::count(std::istreambuf_iterator<char>(infile), 
                    std::istreambuf_iterator<char>(), c);
}

unsigned count4(std::istream &infile, char c) {    
    return std::count(std::istream_iterator<char>(infile), 
                    std::istream_iterator<char>(), c);
}

unsigned int count5(std::istream &infile, char c) {
    static char buffer[8192];
    unsigned int count = 0;

    while (infile.read(buffer, sizeof(buffer)))
        count += std::count(buffer, buffer+infile.gcount(), c);
    count += std::count(buffer, buffer+infile.gcount(), c);
    return count;
}

unsigned count6(std::istream &infile, char c) {
    unsigned int count = 0;
    char ch;

    while (infile >> ch)
        if (ch == c)
            ++count;
    return count;
}

template <class F, class T>
void timer(F f, T &t, std::string const &title) { 
    unsigned count;
    clock_t start = clock();
    count = f(t, 'N');
    clock_t stop = clock();
    std::cout << std::left << std::setw(30) << title << "\tCount: " << count;
    std::cout << "\tTime: " << double(stop-start)/CLOCKS_PER_SEC << "\n";
}

int main() {
    char const *name = "equivs2.txt";

    FILE *infile=fopen(name, "r");

    timer(count1, infile, "ignore");

    rewind(infile);
    timer(count1, infile, "using getc");

    rewind(infile);
    timer(count2, infile, "using fread");

    fclose(infile);

    std::ifstream in2(name);
    timer(count3, in2, "ignore");

    in2.clear();
    in2.seekg(0);
    timer(count3, in2, "using streambuf iterators");

    in2.clear();
    in2.seekg(0);
    timer(count4, in2, "using stream iterators");

    in2.clear();
    in2.seekg(0);
    timer(count5, in2, "using istream::read");

    in2.clear();
    in2.seekg(0);
    timer(count6, in2, "using operator>>");

    return 0;
}

En exécutant ceci, j'obtiens des résultats comme ceci (avec MS VC++):

ignore                          Count: 1300     Time: 0.309
using getc                      Count: 1300     Time: 0.308
using fread                     Count: 1300     Time: 0.028
ignore                          Count: 1300     Time: 0.091
using streambuf iterators       Count: 1300     Time: 0.091
using stream iterators          Count: 1300     Time: 0.613
using istream::read             Count: 1300     Time: 0.028
using operator>>                Count: 1300     Time: 0.619

Et ceci (avec MinGW):

ignore                          Count: 1300     Time: 0.052
using getc                      Count: 1300     Time: 0.044
using fread                     Count: 1300     Time: 0.036
ignore                          Count: 1300     Time: 0.068
using streambuf iterators       Count: 1300     Time: 0.068
using stream iterators          Count: 1300     Time: 0.131
using istream::read             Count: 1300     Time: 0.037
using operator>>                Count: 1300     Time: 0.121

Comme nous pouvons le voir dans les résultats, il ne s'agit pas vraiment d'iostreams étant catégoriquement lent. Plutôt, beaucoup dépend exactement comment vous utilisez iostreams (et dans une moindre mesure FILE * ainsi). Il y a aussi une variation assez importante juste entre ceux-ci à application.

Néanmoins, les versions les plus rapides avec chacune (fread et istream::read) sont essentiellement liées. Avec VC++ getc est un peu plus lent que istream::read ou et istreambuf_iterator.

Bottom line: obtenir de bonnes performances avec iostreams nécessite un peu plus de soin qu'avec FILE * - mais c'est certainement possible. Ils vous donnent également plus d'options: commodité lorsque vous ne vous souciez pas beaucoup de la vitesse et des performances directement compétitives avec le meilleur que vous pouvez obtenir des e/s de style C, avec un peu de travail supplémentaire.

8
répondu Jerry Coffin 2017-07-12 00:44:21

Sur un sujet similaire, STL dit :" Vous pouvez appeler setvbuf() pour activer la mise en mémoire tampon sur stdout."

Https://connect.microsoft.com/VisualStudio/feedback/details/642876/std-wcout-is-ten-times-slower-than-wprintf-performance-bug-in-c-library

0
répondu AndrewDover 2013-09-09 01:03:54

Bien que cette question soit assez ancienne, je suis étonné que personne n'ait mentionné la construction d'objets iostream.

C'est-à-dire, chaque fois que vous créez un STL iostream (et d'autres variantes de flux), si vous entrez dans le code, le constructeur appelle une fonction interne Init. Dans ce cas, operator new est appelé pour créer un nouvel objet locale. Et de même, est détruit sur la destruction.

C'est hideux, à mon humble avis. Et contribue certainement à ralentir la construction/destruction de l'objet, car la mémoire est alloué / désalloué à l'aide d'un verrou système, à un moment donné.

En outre, certains flux STL vous permettent de spécifier un allocator, alors pourquoi le locale n'est-il pas créé en utilisant l'allocateur spécifié?

En utilisant des flux dans un environnement multithread, vous pouvez également imaginer le goulot d'étranglement imposé en appelant operator new chaque fois qu'un nouvel objet stream est construit.

Désordre hideux si vous me demandez, comme je le découvre moi-même en ce moment!

0
répondu dicksters 2018-03-14 19:58:52