Question

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 optimiser 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, et dépendent du disque dur, et qu'il existe peut-être d'autres matériels / logiciels.

(Je dois préciser que je suis un peu novice en Java. Il peut donc s'agir simplement d'un appel d'API Java dont je ne connais pas l'existence.)

Modifier: Je ne connais pas à l'avance le type de système sur lequel ce système sera utilisé. Je ne peux donc pas en supposer beaucoup. (J'utilise Java pour cette raison.)

Modifier: Le code ci-dessus manque des éléments tels que try..catch pour réduire la publication

.
Était-ce utile?

La solution

La taille optimale de la mémoire tampon dépend de plusieurs facteurs: la taille des blocs du système de fichiers, la taille de la mémoire cache du processeur et la latence de la mémoire 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 ( Autrement dit, 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 RAM - > Latence du 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 de la mémoire et de la RAM.

C’est la raison pour laquelle vous voyez la plupart des mémoires tampons d’une puissance é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 assez décalé dans un scénario de streaming typique, car le bloc lu à partir du disque sera toujours en mémoire lorsque vous atteindrez la lecture suivante (nous effectuons des lectures séquentielles ici, après tout) - donc vous finissez par payer la RAM - > Prix ??de latence de la mémoire cache L3 / L2 à la lecture suivante, mais pas la latence du disque et du RAM. En termes d’ordre de grandeur, la latence du disque et de la RAM est si lente qu’elle détruit 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 pense que les choses se stabiliseraient assez rapidement.

Il existe une tonne de conditions et d'exceptions - les complexités du système sont en fait assez renversantes (le simple fait de maîtriser les transferts de cache L3 - > L2 est extrêmement complexe et cela change avec chaque type de processeur).

Cela conduit à la réponse "monde réel": si votre application a 99% de performances, définissez la taille du cache sur 8192 et passez à autre chose (encore mieux, choisissez encapsulation plutôt que 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 aux utilisateurs de tester et d’optimiser système auto-optimisant).

Autres conseils

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

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.

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 certain que cela constitue le goulot d'étranglement de votre application, vous devez alors commencer le profilage pour rechercher la taille de mémoire tampon optimale. 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 de tampon plus grand que la taille de votre cache L2.

Dans l'idéal, nous devrions disposer de suffisamment de mémoire pour lire le fichier en une seule lecture. Ce serait la meilleure performance car nous laissons le système gérer à volonté le système de fichiers, les unités d’allocation et le disque dur. En pratique, vous avez la chance 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 sur NTFS). Et le meilleur de tous: créer un point de repère pour tester plusieurs options.

Vous pouvez utiliser les flux / lecteurs BufferedStream puis utiliser leurs tailles de mémoire tampon.

Je pense que BufferedXStreams utilise 8192 comme taille de tampon, mais comme l’a dit Ovidiu, vous devriez probablement effectuer 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.

La lecture de fichiers à l'aide de FileChannel et MappedByteBuffer de Java NIO donnera probablement lieu à 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.

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 quelques informations supplémentaires, 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.

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é 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 le programme est lié au processeur à l'intérieur de MessageDigest.update (), et la majorité du temps n'est pas passé dans le code de l'application. Il ne sera donc pas utile de le modifier.

(Hmm ... avec plusieurs cœurs, les threads pourraient aider.)

1024 convient à une grande variété de situations, 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 de nombreux facteurs, notamment le blocage du système de fichiers. taille et matériel du processeur.

Il est également courant de choisir une puissance de 2 pour la taille de la mémoire tampon, car Le matériel est structuré avec des blocs fle et des tailles de cache d'une puissance de 2. Le tampon les classes vous permettent de spécifier la taille de la mémoire tampon dans le constructeur. Si aucun n'est fourni, ils utilisez une valeur par défaut, qui correspond à une puissance de 2 dans la plupart des machines virtuelles.

Quelle que soit la taille de la mémoire tampon choisie, la plus grande augmentation de performance que vous obtiendrez see est en train de passer d'un accès sans tampon à un accès en mémoire tampon. Le réglage de la taille de la mémoire tampon peut améliorer les performances légèrement, mais à moins que vous utilisez un extrêmement petit ou extrêmement grande taille de la mémoire tampon, il est peu probable qu’elle ait un impact significatif.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top