J’essayais de lire un fichier dans un tableau en utilisant FileInputStream, et un fichier d’environ 800 Ko prenait environ 3 secondes à lire en mémoire. J’ai ensuite essayé le même code sauf avec le FileInputStream enveloppé dans un BufferedInputStream et il a fallu environ 76 millisecondes. Pourquoi lire un octet par octet est-il tellement plus rapide avec un BufferedInputStream que je le lis toujours octet par octet? Voici le code (le rest du code n’est pas pertinent). Notez que c’est le code “rapide”. Vous pouvez simplement supprimer le BufferedInputStream si vous voulez le code “lent”:
InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(file)); int[] fileArr = new int[(int) file.length()]; for (int i = 0, temp = 0; (temp = is.read()) != -1; i++) { fileArr[i] = temp; }
BufferedInputStream est plus de 30 fois plus rapide. Bien plus que cela. Alors, pourquoi est-ce possible et est-il possible de rendre ce code plus efficace (sans utiliser de bibliothèques externes)?
Dans FileInputStream
, la méthode read()
lit un seul octet. Du code source:
/** * Reads a byte of data from this input stream. This method blocks * if no input is yet available. * * @return the next byte of data, or
-1
if the end of the * file is reached. * @exception IOException if an I/O error occurs. */ public native int read() throws IOException;
Ceci est un appel natif à l’OS qui utilise le disque pour lire l’octet unique. C’est une opération lourde.
Avec un BufferedInputStream
, la méthode délègue à une méthode read()
surchargée qui lit le nombre d’octets 8192
et les met en mémoire tampon jusqu’à ce qu’ils soient nécessaires. Il ne renvoie toujours que le seul octet (mais garde les autres en réserve). De cette façon, BufferedInputStream
effectue moins d’appels natifs vers le système d’exploitation pour lire le fichier.
Par exemple, votre fichier a une longueur de 32768
octets. Pour obtenir tous les octets en mémoire avec un FileInputStream
, vous aurez besoin d’appels natifs 32768
au système d’exploitation. Avec un BufferedInputStream
, vous n’aurez besoin que de 4
, quel que soit le nombre d’appels read()
que vous ferez (toujours 32768
).
En ce qui concerne la façon de le rendre plus rapide, vous pouvez envisager la classe NIO FileChannel
Java 7, mais je n’ai aucune preuve à l’appui.
Un BufferedInputStream enroulé autour d’un FileInputStream demandera des données à partir de FileInputStream en gros morceaux (environ 512 octets par défaut, je pense.) Ainsi, si vous lisez 1000 caractères un à la fois, le FileInputStream n’aura qu’à accéder au disque deux fois. . Cela sera beaucoup plus rapide!
C’est à cause du coût de l’access au disque. Supposons que vous avez un fichier dont la taille est de 8 Ko. 8 * 1024 fois le disque d’access sera nécessaire pour lire ce fichier sans BufferedInputStream.
À ce stade, BufferedStream arrive à la scène et agit comme un intermédiaire entre FileInputStream et le fichier à lire.
D’un seul coup, les morceaux d’octets par défaut sont de 8 ko en mémoire, puis FileInputStream lira les octets de cet intermédiaire. Cela diminuera le temps de l’opération.
private void exercise1WithBufferedStream() { long start= System.currentTimeMillis(); try (FileInputStream myFile = new FileInputStream("anyFile.txt")) { BufferedInputStream bufferedInputStream = new BufferedInputStream(myFile); boolean eof = false; while (!eof) { int inByteValue = bufferedInputStream.read(); if (inByteValue == -1) eof = true; } } catch (IOException e) { System.out.println("Could not read the stream..."); e.printStackTrace(); } System.out.println("time passed with buffered:" + (System.currentTimeMillis()-start)); } private void exercise1() { long start= System.currentTimeMillis(); try (FileInputStream myFile = new FileInputStream("anyFile.txt")) { boolean eof = false; while (!eof) { int inByteValue = myFile.read(); if (inByteValue == -1) eof = true; } } catch (IOException e) { System.out.println("Could not read the stream..."); e.printStackTrace(); } System.out.println("time passed without buffered:" + (System.currentTimeMillis()-start)); }