149 votes

Comment déterminer la taille idéale du tampon lors de l'utilisation de FileInputStream ?

J'ai une méthode qui crée un MessageDigest (un hachage) à partir d'un fichier, et je dois le faire pour un grand nombre de fichiers (>= 100 000). Quelle taille dois-je donner au tampon utilisé pour lire les fichiers afin de maximiser les performances ?

La plupart des gens connaissent le code de base (que je vais répéter ici au cas où) :

MessageDigest md = MessageDigest.getInstance( "SHA" );
FileInputStream ios = new FileInputStream( "myfile.bmp" );
byte[] buffer = new byte[4 * 1024]; // what should this value be?
int read = 0;
while( ( read = ios.read( buffer ) ) > 0 )
    md.update( buffer, 0, read );
ios.close();
md.digest();

Quelle est la taille idéale du tampon pour maximiser le débit ? Je sais que cela dépend du système, et je suis presque sûr que c'est OS, FileSystem, et Cela dépend du disque dur, et il peut y avoir d'autres matériels/logiciels dans le mélange.

(Je dois préciser que je suis quelque peu novice en Java, il se peut donc qu'il s'agisse d'un appel à l'API Java que je ne connais pas).

Edit : Je ne connais pas à l'avance le type de systèmes sur lesquels il sera utilisé, je ne peux donc pas faire de grandes suppositions. (J'utilise Java pour cette raison).

Edit : Le code ci-dessus manque des choses comme try..catch pour rendre le message plus petit.

203voto

Kevin Day Points 9446

La taille optimale de la mémoire tampon est liée à un certain nombre d'éléments : la taille des blocs du système de fichiers, la taille du cache du processeur et la latence du cache.

La plupart des systèmes de fichiers sont configurés pour utiliser des tailles de bloc de 4096 ou 8192. En théorie, si vous configurez la taille de votre tampon de manière à lire quelques octets de plus que le bloc du disque, les opérations avec le système de fichiers peuvent être extrêmement inefficaces (par exemple, si vous configurez votre tampon pour lire 4100 octets à la fois, chaque lecture nécessiterait 2 lectures de blocs par le système de fichiers). Si les blocs sont déjà dans le cache, vous payez le prix de la latence RAM -> cache L3/L2. Si vous êtes malchanceux et que les blocs ne sont pas encore dans le cache, vous payez également le prix de la latence disque->RAM.

C'est pourquoi la plupart des tampons sont dimensionnés comme une puissance de 2, et généralement plus grands que (ou égaux à) la taille du bloc du disque. Cela signifie que l'une de vos lectures de flux peut entraîner la lecture de plusieurs blocs de disque - mais ces lectures utiliseront toujours un bloc complet - pas de lectures inutiles.

Ceci est très largement compensé dans un scénario de streaming typique car le bloc lu sur le disque sera toujours en mémoire lors de la lecture suivante (nous faisons des lectures séquentielles ici, après tout) - vous payez donc le prix de la latence RAM -> cache L3/L2 lors de la lecture suivante, mais pas la latence disque->RAM. En termes d'ordre de grandeur, la latence disque->RAM est si lente qu'elle écrase toute autre latence à laquelle vous pourriez être confronté.

Je pense donc que si vous effectuez un test avec différentes tailles de cache (je ne l'ai pas fait moi-même), vous constaterez probablement un impact important de la taille du cache jusqu'à la taille du bloc du système de fichiers. Au-delà, je pense que les choses se stabiliseraient assez rapidement.

Il existe un tonne de conditions et d'exceptions ici - les complexités du système sont en fait assez stupéfiantes (le simple fait de maîtriser les transferts de cache L3 -> L2 est d'une complexité époustouflante, et cela change avec chaque type de CPU).

Ceci nous amène à la réponse du "monde réel" : Si votre application est comme 99% des autres, fixez la taille du cache à 8192 et passez à autre chose (mieux encore, choisissez l'encapsulation plutôt que les performances et utilisez BufferedInputStream pour cacher les détails). Si vous faites partie des 1% d'applications qui dépendent fortement du débit du disque, concevez votre implémentation de manière à pouvoir échanger différentes stratégies d'interaction avec le disque, et fournissez les boutons et les cadrans pour permettre à vos utilisateurs de tester et d'optimiser (ou mettez au point un système auto-optimisant).

17voto

Jon Skeet Points 692016

Oui, cela dépend probablement de plusieurs facteurs, mais je doute que cela fasse une grande différence. J'ai tendance à opter pour 16K ou 32K comme un bon équilibre entre l'utilisation de la mémoire et les performances.

Notez que vous devriez avoir un bloc try/finally dans le code pour vous assurer que le flux est fermé même si une exception est levée.

7voto

Adam Rosenfield Points 176408

Dans la plupart des cas, cela n'a pas beaucoup d'importance. Choisissez simplement une bonne taille, comme 4K ou 16K, et tenez-vous-en à celle-ci. Si vous êtes positif que c'est le goulot d'étranglement de votre application, alors vous devriez commencer à profiler pour trouver la taille optimale du tampon. Si vous choisissez une taille trop petite, vous perdrez du temps à effectuer des opérations d'E/S et des appels de fonction supplémentaires. Si vous choisissez une taille trop grande, vous commencerez à voir beaucoup de manques dans le cache, ce qui vous ralentira vraiment. N'utilisez pas un tampon plus grand que la taille de votre cache L2.

4voto

Ovidiu Pacurar Points 5129

Dans le cas idéal, nous devrions avoir assez de mémoire pour lire le fichier en une seule opération de lecture. Ce serait la meilleure performance car nous laissons le système gérer le système de fichiers, les unités d'allocation et le disque dur à volonté. En pratique, vous avez la chance de connaître la taille des fichiers à l'avance, utilisez simplement la taille moyenne des fichiers arrondie à 4K (unité d'allocation par défaut sur NTFS). Et le meilleur de tous : créer un benchmark pour tester plusieurs options.

4voto

John Gardner Points 10882

Vous pourriez utiliser les BufferedStreams/readers et ensuite utiliser leurs tailles de tampon.

Je crois que les BufferedXStreams utilisent 8192 comme taille de tampon, mais comme Ovidiu l'a dit, vous devriez probablement faire un test avec un grand nombre d'options. Cela va vraiment dépendre du système de fichiers et de la configuration du disque pour savoir quelles sont les meilleures tailles.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X