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 j'ai besoin de faire cela à beaucoup 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 juste 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 c'est dépendant du système, et je suis sûr que son OS, Système de fichiers, et dépendant du HDD, et il y a peut-être d'autres matériels/logiciels dans le mélange.

(je dois préciser que je suis un peu nouveau pour Java, donc cela peut être simplement une partie de l'API Java appel, je ne sais pas.)

Edit: je ne sais pas à l'avance les types de systèmes, cela va être utilisé, donc je ne peux pas assumer un lot entier. (Je suis en utilisant Java pour cette raison.)

Edit: le code ci-dessus manque des choses comme try..attraper pour rendre le poteau plus petit

127
demandé sur ARKBAN 2008-10-25 23:13:51

9 réponses

la taille optimale du tampon est liée à un certain nombre de choses: la taille du bloc du système de fichiers, la taille du cache CPU et la latence du cache.

la plupart des systèmes de fichiers sont configurés pour utiliser des tailles de blocs de 4096 ou 8192. En théorie, si vous configurez la taille de votre tampon pour lire quelques octets de plus que le bloc disque, les opérations avec le système de fichiers peuvent être extrêmement inefficaces (i.e. si vous avez 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 finissez par payer le prix de la latence de cache RAM -> L3/L2. Si vous êtes malchanceux et les blocs ne sont pas en cache, vous payez le prix du disque->RAM latence.

c'est pourquoi vous voyez la plupart des tampons dimensionnés comme une puissance de 2, et généralement plus grand que (ou égal à) la taille du bloc de disque. Cela signifie que l'un de vos lectures de flux pourrait entraîner des lectures de plusieurs blocs de disques - mais ces lectures utiliseront toujours un bloc complet - Pas de lectures inutiles.

maintenant, c'est un peu offset dans un scénario de diffusion en continu typique parce que le bloc qui est lu à partir du disque va encore être en mémoire quand vous frappez la prochaine lecture (nous faisons des lectures séquentielles ici, après tout) - donc vous finissez par payer le prix RAM -> L3/L2 cache latency sur la prochaine lecture, mais pas le disque->la latence RAM. En termes d'ordre de grandeur, la latence de disque->RAM est si lente qu'elle submerge à peu près toute autre latence que vous pourriez être traiter avec.

donc, je soupçonne que si vous avez effectué un test avec différentes tailles de cache (Je ne l'ai pas fait moi-même), vous trouverez probablement un grand impact de la taille du cache jusqu'à la taille du bloc du système de fichiers. Au-dessus de cela, je soupçonne que les choses se stabiliseraient assez rapidement.

il y a une tonne de conditions et d'exceptions ici - les complexités du système sont en fait assez stupéfiant (juste obtenir une poignée sur L3 - > L2 les transferts de cache sont incroyablement complexes, et changent avec chaque type de CPU).

cela mène à la réponse du "monde réel": si votre application est comme 99% là-bas, mettez la taille du cache à 8192 et passez à autre chose (encore mieux, choisissez encapsulation plutôt que performance et utilisez BufferedInputStream pour cacher les détails). Si vous êtes dans le 1% des applications qui sont fortement dépendants du débit de disque, concevoir votre mise en œuvre de sorte que vous pouvez échanger différentes stratégies d'interaction disque, et fournir les boutons et cadrans pour permettre à vos utilisateurs de tester et d'optimiser (ou de venir avec quelques auto-optimisation du système).

182
répondu Kevin Day 2008-10-27 03:43:45

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 la performance.

noter que vous devriez avoir un essai/enfin bloquer dans le code pour s'assurer que le flux est fermé même si une exception est lancée.

14
répondu Jon Skeet 2008-10-25 19:21:21

dans la plupart des cas, ça n'a pas vraiment d'importance. Il suffit de choisir une bonne taille comme 4K ou 16K et s'en tenir à elle. Si vous êtes positif que c'est le goulot d'étranglement dans votre application, alors vous devriez commencer à profiler pour trouver la taille optimale de 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 fonction supplémentaires. Si vous choisissez une taille qui est trop grande, vous commencerez à voir beaucoup de cache manque qui vous ralentira vraiment vers le bas. N'utilisez pas un tampon plus grand que votre taille de cache L2.

7
répondu Adam Rosenfield 2008-10-25 20:49:46

dans le cas idéal, nous devrions avoir assez de mémoire pour lire le fichier en une opération de lecture. Ce serait la meilleure performance parce que nous laissons le système gérer le système de fichiers , les unités d'attribution et HDD à volonté. En pratique, vous avez la chance de connaître les tailles de fichiers à l'avance, il suffit d'utiliser 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.

4
répondu Ovidiu Pacurar 2008-10-25 20:00:23

lire des fichiers en utilisant le FileChannel de Java NIO et MappedByteBuffer aboutira très probablement à une solution qui sera beaucoup plus rapide que n'importe quelle solution impliquant FileInputStream. Fondamentalement, la mémoire-carte de grands dossiers, et utilisent des tampons directs pour les petits.

4
répondu Alexander 2008-10-25 21:33:37

vous pouvez utiliser les répertoires/lecteurs Buffered puis utiliser leurs tailles de tampon.

je crois que les Bufferedxstream utilisent la taille de tampon 8192, mais comme Ovidiu l'a dit, vous devriez probablement faire un test sur un tas d'options. Son va vraiment dépendre du système de fichiers et des configurations de disque quant à ce que les meilleures tailles sont.

3
répondu John Gardner 2008-10-25 20:29:51

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

après cela, je suppose que la taille du tampon n'a pas vraiment d'importance. Soit le programme est lié à l'E/S, et la taille croissante du tampon par défaut sur BIS, n'aura pas d'impact important sur la performance.

Ou le programme est dépendant du PROCESSEUR à l'intérieur de la MessageDigest.mise à jour(), et la majorité de l'époque n'est pas utilisé dans le code de l'application, de sorte que le peaufinage il ne va pas aider.

(Hum... avec plusieurs noyaux, threads pourrait aider.)

1
répondu Maglob 2008-10-25 21:20:13

dans la source de BufferedInputStream vous trouverez: private static int DEFAULT_BUFFER_SIZE = 8192;

Donc, c'est okey pour vous d'utiliser la valeur par défaut.

Mais si vous pouvez trouver plus d'informations vous obtiendrez des réponses plus valables.

Par exemple, votre adsl peut préférer un tampon de 1454 octets, c'est à cause de la charge utile de TCP/IP. Pour les disques, vous pouvez utiliser une valeur qui correspond à la taille du bloc de votre disque.

1
répondu GoForce5500 2017-01-05 08:33:51

1024 est approprié pour une grande variété de circonstances, bien que dans la pratique, vous pouvez voir de meilleures performances avec une taille de tampon plus ou moins grande.

cela dépend d'un certain nombre de facteurs, y compris le bloc du système de fichiers taille et Matériel CPU.

il est également fréquent de choisir une puissance de 2 pour la taille du tampon, car la plupart des le matériel est structuré avec des blocs fle et des tailles de cache qui sont une puissance de 2. Le Tampon les classes permettent vous devez spécifier la taille du tampon dans le constructeur. Si aucun n'est fourni, ils utilisez une valeur par défaut, qui est une puissance de 2 dans la plupart des JVM.

quelle que soit la taille de la mémoire tampon que vous choisissez, la plus grande augmentation de performance que vous obtiendrez voir est en train de passer de l'accès non tamponné à l'accès au fichier tamponné. L'ajustement de la taille du tampon peut améliorer légèrement les performances, mais à moins que vous n'utilisiez un grande taille de tampon, il est peu probable qu'elle ait un impact significatif.

0
répondu Adrian Krebs 2017-01-05 08:06:48