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 :

Type de fonction système derrière un fstream.read()


Sujet :

SL & STL C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Rédacteur

    Avatar de Matthieu Brucher
    Profil pro
    Développeur HPC
    Inscrit en
    Juillet 2005
    Messages
    9 810
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur HPC
    Secteur : Industrie

    Informations forums :
    Inscription : Juillet 2005
    Messages : 9 810
    Par défaut Type de fonction système derrière un fstream.read()
    Hello à tous,

    D'après certaines documentations, la fonction ifstream.read() lit un bloc de données. Or, il s'avère que mon code qui se base sur cette fonction passerait par des mmap(), ce qui a dans mon cas des performances déplorables.

    Y a-t-il moyen de savoir comment les bibliothèques système sont implémentées à ce niveau ? Et est-il possible de forcer leur comportement ?

  2. #2
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Salut,

    Je ne connais pas l'implémentation linuxienne par contre j'ai inspecté il y a quelques temps l'implémentation de la bibliothèque standard IOstream fournie avec Visual Studio 2010 (donc basée sur celle de Dinkumware), ce qui permet au moins de faire un parallèle...

    En résumé, c'est à dire en omettant de nombreux appels de fonctions intermédiaires on a :

    Constructeur de ifstream (quel que soit le mode)
    Appelle fopen()
    Qui appelle CreateFile()

    Lecture (que ce soit par un std::getline ou par la fonction membre read())
    Appelle fgetc()
    Qui appelle Readfile() (qui lit par bloc de 4096 octets)

    La biblio standard C de MSVC - et donc la biblio standard C++ - ne semble donc jamais utiliser de fichier mappé. Quel que soient les modes d'ouverture ou la manière de lire le fichier on finit toujours pas retomber sur de la lecture bufferisée avec CreateFile + ReadFile.

    Donc pour répondre aux questions, sur Windows :
    Y a-t-il moyen de savoir comment les bibliothèques système sont implémentées à ce niveau ?
    Ben perso, j'avais fait du debug en pas en pas dans les sources de la lib C et C++.
    Et est-il possible de forcer leur comportement ?
    Je n'ai pas croisé de switch/define permettant de changer le comportement dans les sources.

  3. #3
    Rédacteur

    Avatar de Matthieu Brucher
    Profil pro
    Développeur HPC
    Inscrit en
    Juillet 2005
    Messages
    9 810
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur HPC
    Secteur : Industrie

    Informations forums :
    Inscription : Juillet 2005
    Messages : 9 810
    Par défaut
    On passe par un read de 4ko ?? C'est complètement débile !
    Dans mon cas, je fais une demande de 18Mo environ, donc si ça passe par des 4ko, on n'a pas fini... Derrière, le serveur de fichier lit automatiquement par tranches de 8Mo. Comment mettre à la poubelle plus de 7.9Mo à la poubelle avec une implémentation débile (désolé, je suis sur les dents avec des conneries pareilles )

    Merci pour l'info, j'imagine que Linux ne doit malheureusement pas être trop différent...

  4. #4
    Membre Expert
    Avatar de méphistopheles
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    1 551
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 1 551
    Par défaut
    je me souviens avoir fait des tests comparatif sur la vitesse de lecture d'un fichier en mode
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    while (file>>a>>b>>c) 
     [...]
    et j'avais un facteur 10 entre le sdk de visualC++2005 et gcc (je me rapelle plus le numéro de version). (gcc plus rapide)

    D'un autre coté, ça reste quand même tres lent, et je ne sais plus ou j'avais lu que les flux de la stl étaient mal implémentés... donc mieux vaut peut-être prendre une fonction spécialisée.
    Sinon, il me semble que ce chapitre du méga cours de C++ décris en détail l'implémentation des flux dans la stl. Je l'avais parcouru, mais j'avoue que cela m'est quelque-peu sorti de la tête

    bonne chance

  5. #5
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Citation Envoyé par Matthieu Brucher Voir le message
    On passe par un read de 4ko ?? C'est complètement débile !
    Dans mon cas, je fais une demande de 18Mo environ, donc si ça passe par des 4ko, on n'a pas fini... Derrière, le serveur de fichier lit automatiquement par tranches de 8Mo. Comment mettre à la poubelle plus de 7.9Mo à la poubelle avec une implémentation débile (désolé, je suis sur les dents avec des conneries pareilles )
    En creusant un peu, je me suis rendu compte que l'on pouvait changer la taille du buffer de lecture :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    #include <fstream>
     
    int main()
    {
       // file.dat est un fichier de plusieurs dizaines de Mo.
       std::ifstream file("file.dat", std::ios::binary); 
       std::filebuf* fbuf = file.rdbuf();
       fbuf->pubsetbuf(NULL, 8192);
     
       char* buffer = new char[16384]; 
       file.read(buffer, 16384); // 2 ReadFile
    }
    Passer un pointeur nul et une taille à pubsetbuf indique au filebuf qu'il doit continuer à utiliser son buffer interne mais qu'il doit le redimensionner. Et au final pubsetbuf appelle setvbuf de la biblio standard C sur la stream C sous-jacente au filebuf. (Et setvbuf fait un free sur le buffer de la stream puis un malloc à la nouvelle taille).

    Lors de la lecture on vérifie bien que ReadFile lit maintenant par bloc de 8192 au lieu du 4096 par défaut.

    Maintenant, perso je ne sais pas du tout comment la lecture de fichier est géré au niveau système donc je n'ai pas la moindre idée si l'augmentation de la taille du buffer est bénéfique ou pas.
    Faut tester.

  6. #6
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Citation Envoyé par Arzar Voir le message
    Faut tester.
    J'ai cédé à la curiosité

    Le bench suivant, réalisé sous Windows 7 avec VS2010, lit un fichier "file.txt" de 32 Mo en tranches de 8 Mo, et ce de quatre manière différentes :
    1) Avec un ifstream.read(). Le buffer de lecture par défaut est de 4096 octets.
    2) avec un ifstream.read dont le buffer de lecture a été redimensionné à 8 Mo.
    3) Avec la fonction native Win32 ReadFile()
    4) Avec des fichiers mappés.
    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
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    #include <Windows.h>
    #include <iostream>
    #include <fstream>
    #include <boost/iostreams/device/mapped_file.hpp>
     
    const int NB_LOOP = 100;
    const long long SIZE_BUFFER = 8388608; // 8mo
     
    void foo1()
    {
       char* buffer = new char[SIZE_BUFFER];
     
       for(int i = 0 ; i < NB_LOOP; i++)
       {
          std::ifstream file("file.txt", std::ios::binary);
          file.read(buffer, SIZE_BUFFER);	
          file.read(buffer, SIZE_BUFFER); 
          file.read(buffer, SIZE_BUFFER); 
          file.read(buffer, SIZE_BUFFER); 
          // 32 Mo
       }
    }
     
    void foo2()
    {
       char* buffer = new char[SIZE_BUFFER]; 
       char* internal_buffer = new char[SIZE_BUFFER]; 
     
       for(int i = 0 ; i < NB_LOOP; i++)
       {
          std::ifstream file("file.txt", std::ios::binary);
          std::filebuf* fbuf = file.rdbuf();
          fbuf->pubsetbuf(internal_buffer, SIZE_BUFFER);
     
          file.read(buffer, SIZE_BUFFER);
          file.read(buffer, SIZE_BUFFER);	
          file.read(buffer, SIZE_BUFFER);	
          file.read(buffer, SIZE_BUFFER);	
          //32 Mo
       }
    }
     
    void foo3()
    {
       char* buffer = new char[SIZE_BUFFER];
     
       for(int i = 0 ; i < NB_LOOP; i++)
       {
          DWORD nbByteRead;
          HANDLE h = CreateFile(L"file.txt", GENERIC_READ, NULL, NULL,  OPEN_EXISTING, NULL, NULL);
          ReadFile(h, buffer, SIZE_BUFFER, &nbByteRead, NULL);
          ReadFile(h, buffer, SIZE_BUFFER, &nbByteRead, NULL); 
          ReadFile(h, buffer, SIZE_BUFFER, &nbByteRead, NULL); 
          ReadFile(h, buffer, SIZE_BUFFER, &nbByteRead, NULL); 
          // 32 Mo
          CloseHandle(h);
       }
    }
     
    void foo4()
    {
       char* buffer = new char[SIZE_BUFFER];
     
       for(int i = 0 ; i < NB_LOOP; i++)
       {
          boost::iostreams::mapped_file file("file.txt");
          memcpy(buffer, file.data() + 0 * SIZE_BUFFER, SIZE_BUFFER); 
          memcpy(buffer, file.data() + 1 * SIZE_BUFFER, SIZE_BUFFER);
          memcpy(buffer, file.data() + 2 * SIZE_BUFFER, SIZE_BUFFER);
          memcpy(buffer, file.data() + 3 * SIZE_BUFFER, SIZE_BUFFER);
          // 32 Mo
       }
    }
     
    int main()
    {
       int begin = GetTickCount();
       foo1();
       int end = GetTickCount();
     
       std::cout << "ifstream default buffer (4096o) : " << end - begin << "\n";
     
       begin = GetTickCount();
       foo2();
       end = GetTickCount();
     
       std::cout << "ifstream custom buffer (8 Mo) : " << end - begin << "\n";
     
       begin = GetTickCount();
       foo3();
       end = GetTickCount();
     
       std::cout << "Win32 ReadFile (no buffer) : " << end - begin << "\n";
     
       begin = GetTickCount();
       foo4();
       end = GetTickCount();
     
       std::cout << "Mapped file (no buffer) : " << end - begin << "\n";
    }
    Et l'on obtient :
    ifstream default buffer (4096o) : 5991
    ifstream custom buffer (8 Mo) : 5444
    Win32 ReadFile (no buffer) : 3229
    Mapped file (no buffer) : 2980
    (Ce sont des moyennes, car les timings fluctuent beaucoup)

  7. #7
    Rédacteur

    Avatar de Matthieu Brucher
    Profil pro
    Développeur HPC
    Inscrit en
    Juillet 2005
    Messages
    9 810
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur HPC
    Secteur : Industrie

    Informations forums :
    Inscription : Juillet 2005
    Messages : 9 810
    Par défaut
    Merci pour l'info. C'est sans doute plus portable comme écriture, mais elle est bien plus complexe que le pread/pwrite sous Linux...
    Disons que l'intérêt de charger les données dépend de l'application. Dans mon cas, j'utilise toute la donnée immédiatement, donc autant me la donner. Le système disque lit beaucoup plus de données que les 4k qui me sont retournés, et j'ai besoin du reste. Au lieu de faire des dizaines d'accès, autant qu'il n'en fasse qu'un seul

  8. #8
    Membre Expert
    Avatar de méphistopheles
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    1 551
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 1 551
    Par défaut
    je n'ai pas résisté et j'ai fait un test sur linux (netbook donc petit hardware).
    le code porté est:
    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
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    #include <iostream>
    #include <fstream>
    #include <time.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <boost/iostreams/device/mapped_file.hpp>
    #include <boost/date_time/posix_time/posix_time.hpp>
     
    const int NB_LOOP = 100;
    const long long SIZE_BUFFER = 8388608; // 8mo
     
    void foo1()
    {
       char* buffer = new char[SIZE_BUFFER];
     
       for(int i = 0 ; i < NB_LOOP; i++)
       {
          std::ifstream file("file.txt", std::ios::binary);
          file.read(buffer, SIZE_BUFFER);
          file.read(buffer, SIZE_BUFFER);
          file.read(buffer, SIZE_BUFFER);
          file.read(buffer, SIZE_BUFFER);
          // 32 Mo
       }
    }
     
    void foo2()
    {
       char* buffer = new char[SIZE_BUFFER];
       char* internal_buffer = new char[SIZE_BUFFER];
     
       for(int i = 0 ; i < NB_LOOP; i++)
       {
          std::ifstream file("file.txt", std::ios::binary);
          std::filebuf* fbuf = file.rdbuf();
          fbuf->pubsetbuf(internal_buffer, SIZE_BUFFER);
     
          file.read(buffer, SIZE_BUFFER);
          file.read(buffer, SIZE_BUFFER);
          file.read(buffer, SIZE_BUFFER);
          file.read(buffer, SIZE_BUFFER);
          //32 Mo
       }
    }
     
    void foo3()
    {
       char* buffer = new char[SIZE_BUFFER];
     
     
       for(int i = 0 ; i < NB_LOOP; i++)
       {
          int fd=open("file.txt", O_RDONLY);
          off_t offset=0;
          pread(fd, buffer, SIZE_BUFFER, offset);
          offset+=SIZE_BUFFER;
          pread(fd, buffer, SIZE_BUFFER,  offset);
          offset+=SIZE_BUFFER;
          pread(fd, buffer, SIZE_BUFFER,  offset);
          offset+=SIZE_BUFFER;
          pread(fd, buffer, SIZE_BUFFER,  offset);
          offset+=SIZE_BUFFER;
          // 32 Mo
          close(fd);
       }
    }
     
    void foo4()
    {
       char* buffer = new char[SIZE_BUFFER];
     
       for(int i = 0 ; i < NB_LOOP; i++)
       {
          boost::iostreams::mapped_file file("file.txt");
          memcpy(buffer, file.data() + 0 * SIZE_BUFFER, SIZE_BUFFER);
          memcpy(buffer, file.data() + 1 * SIZE_BUFFER, SIZE_BUFFER);
          memcpy(buffer, file.data() + 2 * SIZE_BUFFER, SIZE_BUFFER);
          memcpy(buffer, file.data() + 3 * SIZE_BUFFER, SIZE_BUFFER);
          // 32 Mo
       }
    }
     
    int main()
    {
       boost::posix_time::ptime begin(
        boost::posix_time::microsec_clock::local_time());
       foo1();
       boost::posix_time::ptime end(
        boost::posix_time::microsec_clock::local_time());
       boost::posix_time::time_period duration(begin ,end);
       std::cout << "ifstream default buffer (4096o) : " <<
        duration.length().total_milliseconds() << "\n";
     
       begin = boost::posix_time::microsec_clock::local_time();
       foo2();
       end = boost::posix_time::microsec_clock::local_time();
       duration=boost::posix_time::time_period(begin ,end);
       std::cout << "ifstream custom buffer (8 Mo) : " <<
        duration.length().total_milliseconds() << "\n";
     
       begin = boost::posix_time::microsec_clock::local_time();
       foo3();
       end = boost::posix_time::microsec_clock::local_time();
       duration=boost::posix_time::time_period(begin ,end);
       std::cout << "unix ReadFile (no buffer) : " <<
        duration.length().total_milliseconds() << "\n";
     
       begin = boost::posix_time::microsec_clock::local_time();
       foo4();
       end = boost::posix_time::microsec_clock::local_time();
       duration=boost::posix_time::time_period(begin ,end);
       std::cout << "Mapped file (no buffer) : " <<
        duration.length().total_milliseconds() << "\n";
    }
    les résultats sont complètement différents:
    Citation Envoyé par résultats en ms
    ifstream default buffer (4096o) : 4999
    ifstream custom buffer (8 Mo) : 4975
    unix ReadFile (no buffer) : 4981
    Mapped file (no buffer) : 6785
    ici, peu de différence entre le ifstream et le pread ... peut-être que j'ai mal codé, mais j'opterais plutot pour une mauvaise implémentation vc++. Par contre boost se fait complètement manger... peut-être par-ce que je suis en -O3

    faudrait faire des tests sur la même machine mais bon, la flemme de passer sous windows pour être sûr.

    un troisième testeur ?

Discussions similaires

  1. Typed Scheme (Un système de type pour Scheme)
    Par alex_pi dans le forum Scheme
    Réponses: 27
    Dernier message: 27/01/2008, 06h28
  2. type et fonction return
    Par acacia dans le forum Débuter
    Réponses: 11
    Dernier message: 23/01/2008, 12h09
  3. Probleme sur un ensemble de type dans fonction
    Par jetgirl dans le forum Oracle
    Réponses: 4
    Dernier message: 19/02/2007, 13h04
  4. type de fonction
    Par sidahmed dans le forum C
    Réponses: 4
    Dernier message: 20/03/2006, 13h50
  5. [Système] Employer les fonctions systèmes ?
    Par Blo0d4x3 dans le forum API standards et tierces
    Réponses: 6
    Dernier message: 31/12/2005, 14h32

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