Previous Up Next

B.5.4  Toujours plus loin : entrées-sorties bufferisées

La documentation recommande l’emploi de flux bufferisés (buffered désolé pas de terme français adéquat) pour atteindre l’efficacité maximale « top efficiency ». L’idée des flux bufferisés, qui est très générale, est la suivante : l’écriture ou la lecture effective de caractères a un coût fixe important, qui est payé quelque soit le nombre de caractères effectivement transférés entre fichier et programme. Plus précisément, écrire ou lire n caractères coûte de l’ordre de K0 + K1·n, où K0 est bien plus grand que K1. Cela peut s’expliquer par divers phénomènes. Par exemple, un appel au système d’exploitation est relativement lent, et une opération d’entrée-sortie signifie un seul appel système, quelque soit le nombre de caractères impliqués. Ou encore, le coût des transferts entre disque et mémoire est largement indépendant du nombre de caractères transférés, jusqu’à une certaine taille, de par la nature même du dispositif physique « disque » qui lit et écrit des données par blocs de taille fixée.

Pour fixer les idées, prenons l’exemple de l’écriture. L’idée est alors de ne pas écrire effectivement chaque caractère en réaction à un appel out.write(c) mais à la place de le ranger dans une zone mémoire appelée tampon (buffer). Le tampon a une taille fixe, et les caractères du tampon sont effectivement transmis au système d’exploitation quand le tampon est plein. De cette façon le coût fixe K0 est payé moins souvent et l’efficacité totale est améliorée. Les transferts vers les fichiers à travers un tampon mémoire présentent l’inconvénient que le caractère c peut ne pas se trouver dans le fichier2 dès que l’on appelle out.write(c). L’effet est particulièrement gênant à la fin du programme. Si le tampon n’est pas vidé (flushed), son contenu est perdu. En effet, le tampon est de la mémoire appartenant au programme et qui donc disparaît avec lui. Il en résulte généralement que la fin du flux ne se retrouve pas dans le fichier. Il faut donc vider le tampon avant de terminer le programme, ce que fait la méthode close() de fermeture des flux, avant de fermer effectivement le flux. On peut aussi vider le tampon plus directement en appelant méthode flush() des flux bufferisés. L’existence d’un retard entre ce qui est écrit par le programme dans le flux et ce qui est effectivement écrit dans le fichier donne donc une raison supplémentaire de fermer les fichiers.

Pour ce qui est d’un fichier ouvert en lecture, la technique du tampon s’applique également, avec les mêmes bénéfices en terme d’efficacité. Dans ce cas, les lectures (read) se font dans le tampon, qui est rempli à partir du fichier quand une demande de lecture trouve un tampon vide. Il y a alors bien entendu une avance à la lecture mais ce décalage ne pose pas les mêmes problèmes que le retard à l’écriture.

Un flux bufferisé en écriture (resp. lecture) est un objet de la classe BufferedWriter (resp. BufferedReader), qui se construit simplement à partir d’un Writer (resp. Reader), et qui reste un Writer (resp. Reader). Voici une autre version Cp2 (source Cp2.java) de la commande cp écrite en Java, qui emploie les entrées-sorties bufferisées.

import java.io.* ;
class Cp2 {
  public static void main(String [] arg) {
    String name1 = arg[0], name2 = arg[1] ;
    try {
      Reader in = new BufferedReader (new FileReader(name1)) ;
      Writer out = new BufferedWriter (new FileWriter (name2)) ;
      for ( ; ; ) {
        int c = in.read() ;
        if (c == -1) break ;
        out.write(c) ;
      }
      out.close() ; in.close() ;
    } catch (IOException e) {
      System.err.println("Malaise : " + e.getMessage()) ;
      System.exit(2) ;
    }
  }
}

Une mesure rapide des temps d’exécution montre que le programme Cp2 est à peu près trois fois plus rapide que Cp.

Les flux bufferisés BufferedWriter et BufferedReader offrent aussi une vue des fichier texte comme étant composés de lignes. Il existe une méthode Newline pour écrire une fin le ligne, et une méthode readLine pour lire une ligne. On utilise souvent BufferedReader pour cette fonctionnalité de lecture ligne à ligne. Il faut aussi noter que les lignes sont définies indépendamment de leur réalisation par les divers systèmes d’exploitation (voir B.3.2.3).


2
Ou plus exactement c n’est pas dans les tampons du système d’exploitation, en route vers le fichier.

Previous Up Next