web-dev-qa-db-fra.com

L'écriture de fichiers C ++ ofstream utilise-t-elle un tampon?

Voici deux programmes qui écrivent 50 000 000 octets dans un fichier.

Le premier programme, écrit en C, utilise un tampon, qui une fois rempli à une valeur arbitraire, écrit sur le disque, puis répète ce processus jusqu'à ce que les 50 000 000 octets soient écrits. J'ai remarqué qu'en augmentant la taille du tampon, le programme prenait moins de temps à s'exécuter. Par exemple, à BUFFER_SIZE = 1, le programme a pris ~ 88,0463 secondes, tandis qu'à BUFFER_SIZE = 1024, le programme n'a pris que ~ 1,7773 secondes. Le meilleur moment que j'ai enregistré était quand BUFFER_SIZE = 131072. Comme BUFFER_SIZE augmentait plus haut que cela, j'ai remarqué que cela commençait à prendre un peu plus de temps.

Le deuxième programme, écrit en C++, utilise ofstream pour écrire un octet à la fois. À ma grande surprise, le programme n'a pris que ~ 1,87 secondes pour s'exécuter. Je m'attendais à ce que cela prenne environ une minute, comme le programme C avec BUFFER_SIZE = 1. Évidemment, le flux C++ gère l'écriture de fichiers différemment que je ne le pensais. Selon mes données, il fonctionne de manière assez similaire au fichier C avec BUFFER_SIZE = 512. Utilise-t-il une sorte de tampon en arrière-plan?

Voici le programme C:

const int NVALUES = 50000000; //#values written to the file
const char FILENAME[] = "/tmp/myfile";
const int BUFFER_SIZE = 8192; //# bytes to fill in buffer before writing

main()
{
    int fd;  //File descriptor associated with output file
    int i;
    char writeval = '\0';
    char buffer[BUFFER_SIZE];

    //Open file for writing and associate it with the file descriptor
    //Create file if it does not exist; if it does exist truncate its size to 0
    fd = open(FILENAME, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);

    for(i=0;i<NVALUES;i++)
    {
        //Package bytes into BUFFER_SIZE chunks 
                //and write a chunk once it is filled
        buffer[i%BUFFER_SIZE] = writeval;
        if((i%BUFFER_SIZE == BUFFER_SIZE-1 || i == NVALUES-1))
            write(fd, buffer, i%BUFFER_SIZE+1);

    }

    fsync(fd);

    close(fd);
}

Voici le programme C++:

int main()
{
    ofstream ofs("/tmp/iofile2");
    int i;

    for(i=0; i<50000000; i++)
        ofs << '\0';

    ofs.flush();
    ofs.close();

    return 0;
}

Merci pour votre temps.

28
user1077685

Oui, les ostreams utilisent un tampon de flux, une sous-classe d'une instanciation du modèle basic_streambuf. L'interface de basic_streambuf est conçue de manière à ce qu'une implémentation puisse faire un tampon s'il y a un avantage à cela.

Il s'agit cependant d'un problème de qualité de mise en œuvre. Les implémentations ne sont pas nécessaires pour ce faire, mais toute implémentation compétente le sera.

Vous pouvez tout lire à ce sujet dans le chapitre 27 de la norme ISO, bien qu'une source plus lisible soit La bibliothèque standard C++: un tutoriel et une référence (- recherche google ).

11
bames53

Oui, toutes les opérations de flux sont mises en mémoire tampon, bien que par défaut, l'entrée, la sortie et la sortie d'erreur standard ne le soient pas de sorte que les interactions avec le C IO soient moins surprenantes.

Comme déjà mentionné, il existe une classe de base streambuf qui est utilisée en arrière-plan. Il est fourni avec son propre tampon, dont la taille est un détail d'implémentation.

Vous pouvez vérifier (expérimentalement) la quantité de ce tampon en utilisant streambuf::in_avail , en supposant que le flux de fichiers d'entrée et le flux de fichiers de sortie sont configurés avec la même taille de tampon ...

Vous pouvez effectuer deux autres opérations ici qui pourraient vous intéresser:

  • vous pouvez changer l'objet streambuf utilisé par un flux, pour passer à une version personnalisée
  • vous pouvez changer le tampon utilisé par l'objet streambuf

les deux doivent être effectuées soit juste après la création du flux, soit après un flush, de peur que certaines données ne soient perdues ...

Pour illustrer le changement de tampon, consultez streambuf::putsetbuf :

#include <fstream>
#include <vector>

int main () {
  std::vector<char> vec(512);

  std::fstream fs;
  fs.rdbuf()->pubsetbuf(&vec.front(), vec.size());

  // operations with file stream here.
  fs << "Hello, World!\n";

  // the stream is automatically closed when the scope ends, so fs.close() is optional
  // the stream is automatically flushed when it is closed, so fs.flush() is optional

  return 0;
}

Vous pouvez maintenant répéter les expériences que vous avez faites en C pour trouver le point idéal :)

12
Matthieu M.

Par this , ofstream a un pointeur interne filebuf, peut être lu via la fonction rdbuf, qui pointe vers un streambuf objet, qui est ceci:

streambuf les objets sont généralement associés à une séquence de caractères spécifique, à partir de laquelle ils lisent et écrivent des données via un tampon de mémoire interne. Le tampon est un tableau en mémoire qui devrait être synchronisé en cas de besoin avec le contenu physique de la séquence de caractères associée.

J'ai mis en gras les bits importants, il semble qu'il utilise un tampon, mais je ne sais pas ou je n'ai pas découvert de quel type de tampon il s'agit.

2
Tony The Lion