web-dev-qa-db-fra.com

Comment déterminez-vous la taille de tampon idéale 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 beaucoup de fichiers (> = 100 000). Quelle est la taille de la mémoire tampon utilisée pour lire les fichiers afin d'optimiser 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 de la mémoire tampon pour maximiser le débit? Je sais que cela dépend du système, et je suis presque sûr que son système d'exploitation, FileSystem, and HDD sont dépendants et qu'il existe peut-être d'autres matériels/logiciels dans le mix. 

(Je dois préciser que je suis un peu nouveau dans Java, alors il peut s'agir simplement d'un appel d'API Java que je ne connais pas.)

Edit: / Je ne sais pas à l'avance quels types de systèmes seront utilisés, je ne peux donc pas en supposer beaucoup. (J'utilise Java pour cette raison.)

Edit: Le code ci-dessus manque des éléments tels que try..catch pour rendre le post plus petit

132
ARKBAN

La taille optimale de la mémoire tampon est liée à plusieurs facteurs: 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 de disque, les opérations avec le système de fichiers peuvent s'avérer extrêmement inefficaces (c.-à-d. configuré votre tampon pour lire 4100 octets à la fois, chaque lecture nécessiterait 2 lectures de bloc par le système de fichiers). Si les blocs sont déjà en cache, vous finirez par payer le prix de RAM -> latence du cache L3/L2. Si vous êtes malchanceux et que les blocs ne sont pas encore en cache, vous payez également le prix de la latence disque-> RAM.

C'est pourquoi vous voyez la plupart des mémoires tampons d'une taille égale à 2 et généralement supérieures (ou égales à) à la taille du bloc de disque. Cela signifie que l'une de vos lectures de flux peut entraîner plusieurs lectures de bloc de disque - mais ces lectures utilisent toujours un bloc complet - aucune lecture perdue.

Maintenant, cela est un peu compensé dans un scénario de streaming typique car le bloc qui est lu à partir du disque sera toujours en mémoire lorsque vous frappez la lecture suivante (nous lisons des séquences séquentielles ici, après tout) - vous finissez donc payer le prix de latence du cache RAM -> L3/L2 à la lecture suivante, mais pas le temps de latence disque-> RAM. En termes d’ordre de grandeur, la latence disque-> RAM est si lente qu’elle gêne quasiment toute autre latence à laquelle vous pourriez être confronté.

Donc, je suppose que si vous exécutez un test avec différentes tailles de cache (vous ne l’avez pas fait moi-même), vous constaterez probablement un impact important sur la taille du cache jusqu’à la taille du bloc du système de fichiers. Au-dessus de cela, je suppose que les choses se stabiliseraient assez rapidement.

Il y a tonne de conditions et d'exceptions ici - la complexité du système est en fait assez renversante (il est extrêmement complexe de savoir comment gérer les transferts de cache L3 -> L2, qui change avec chaque type de processeur).

Cela conduit à la réponse «dans le monde réel»: si votre application compte 99% d'utilisateurs, définissez la taille du cache sur 8192, puis passez à autre chose (mieux encore, choisissez l'encapsulation au-dessus des performances et utilisez BufferedInputStream pour masquer les détails). Si vous êtes dans le 1% des applications qui dépendent fortement du débit de disque, définissez votre implémentation de manière à pouvoir échanger différentes stratégies d’interaction de disque et à fournir les boutons et molettes permettant à vos utilisateurs de tester et d’optimiser système auto-optimisant).

187
Kevin Day

Oui, cela dépend probablement de diverses choses - 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 devez avoir un bloc try/finally dans le code pour vous assurer que le flux est fermé même si une exception est levée.

14
Jon Skeet

Dans la plupart des cas, cela n'a vraiment pas beaucoup d'importance. Il suffit de choisir une bonne taille, comme 4K ou 16K, et de s’y tenir. Si vous êtes positif qu'il s'agit du goulot d'étranglement de votre application, vous devez alors commencer le profilage pour trouver la taille optimale de la mémoire tampon. Si vous choisissez une taille trop petite, vous perdrez du temps à effectuer des opérations d’E/S supplémentaires et des appels de fonctions supplémentaires. Si vous choisissez une taille trop grande, vous constaterez de nombreuses erreurs de mémoire cache qui vous ralentiront vraiment. N'utilisez pas un tampon plus grand que la taille de votre cache L2.

7
Adam Rosenfield

Dans l'idéal, nous devrions disposer de suffisamment de mémoire pour lire le fichier en une seule opération . Ce serait le meilleur outil, 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 êtes Heureux de connaître la taille des fichiers à l'avance, utilisez simplement une taille de fichier moyenne arrondie à 4 Ko (unité d'allocation par défaut sous NTFS) ..__ Et, mieux encore: créez un point de référence pour tester plusieurs options. 

4
Ovidiu Pacurar

Vous pouvez utiliser les bufferedStreams/players puis leurs tailles de mémoire tampon.

Je crois que BufferedXStreams utilise 8192 comme taille de la mémoire tampon, mais comme Ovidiu l’a dit, vous devriez probablement exécuter un test sur de nombreuses options. Cela dépendra vraiment du système de fichiers et de la configuration du disque pour déterminer les meilleures tailles.

4
John Gardner

La lecture de fichiers à l'aide de FileChannel et MappedByteBuffer de Java NIO aboutira probablement à une solution beaucoup plus rapide que toute solution impliquant FileInputStream. Fondamentalement, mappez en mémoire les fichiers volumineux et utilisez des tampons directs pour les plus petits.

4
Alexander

Dans la source de BufferedInputStream, vous trouverez: private static int DEFAULT_BUFFER_SIZE = 8192;
Vous pouvez donc utiliser cette valeur par défaut.
Mais si vous pouvez trouver plus d’informations, vous obtiendrez des réponses plus utiles.
Par exemple, votre adsl peut préférer un tampon de 1454 octets, car c'est la charge utile de TCP/IP. Pour les disques, vous pouvez utiliser une valeur correspondant à la taille de bloc de votre disque.

1
GoForce5500

Comme déjà mentionné dans d'autres réponses, utilisez BufferedInputStreams.

Après cela, je suppose que la taille de la mémoire tampon n'a pas vraiment d'importance. Soit le programme est lié aux E/S, et l’augmentation de la taille de la mémoire tampon par rapport à la valeur par défaut de BIS n’aura pas d’impact considérable sur les performances.

Ou bien le programme est lié à la CPU dans MessageDigest.update (), et la majorité du temps n'est pas passé dans le code de l'application.

(Hmm ... avec plusieurs noyaux, les threads pourraient aider.)

1
Maglob

1024 convient à une grande variété de circonstances, même si dans la pratique, vous pouvez constater de meilleures performances avec une taille de mémoire tampon plus grande ou plus petite. 

Cela dépend d'un certain nombre de facteurs, notamment la taille du bloc du système de fichiers et le matériel de la CPU.

Il est également courant de choisir une puissance de 2 pour la taille de la mémoire tampon, car la plupart des matériels Sous-jacents sont structurés avec des tailles de blocs et de mémoires cache de 2. dans le constructeur. Si aucun n'est fourni, ils utilisent une valeur par défaut, qui est une puissance de 2 dans la plupart des machines virtuelles.

Quelle que soit la taille de la mémoire tampon choisie, l’augmentation de performance la plus importante que vous constaterez sera de passer d’un accès sans tampon à un fichier en mémoire tampon. Le fait d’ajuster la taille de la mémoire tampon peut améliorer légèrement les performances, mais si vous utilisez une taille de mémoire tampon extrêmement petite ou extrêmement grande, il est peu probable qu’elle ait un impact significatif.

0
Adrian Krebs