Performance / stabilité d'un mémoire fichier mappées - natif ou MappedByteBuffer - vs plaine ol » FileOutputStream

StackOverflow https://stackoverflow.com/questions/537295

Question

Je soutiens une application Java héritée qui utilise des fichiers plats (texte brut) pour la persistance. En raison de la nature de l'application, la taille de ces fichiers peut atteindre 100s Mo par jour, et souvent le facteur limitant la performance des applications est un fichier IO. À l'heure actuelle, l'application utilise une plaine ol » java.io.FileOutputStream pour écrire des données sur le disque.

Récemment, nous avons eu plusieurs développeurs affirment que l'utilisation des fichiers mappés en mémoire, mis en œuvre dans le code natif (C / C ++) et accessibles via JNI, fournirait de meilleures performances. Cependant, FileOutputStream utilise déjà des méthodes natives pour ses méthodes de base (à savoir d'écriture (octet [])), il semble donc une hypothèse ténue sans données précises ou des preuves au moins anecdotiques.

J'ai plusieurs questions à ce sujet:

  1. Cette affirmation est vraiment vrai? Est-ce que les fichiers mémoire mappée toujours fournir plus rapidement IO par rapport à Java de FileOutputStream?

  2. Est-ce que la classe MappedByteBuffer accessible à partir d'un FileChannel fournir les mêmes fonctionnalités que natif mémoire bibliothèque de fichiers mappé accessible via JNI? Qu'est-ce que MappedByteBuffer manque qui pourrait vous conduire à utiliser un solution JNI?

  3. Quels sont les risques de l'utilisation fichiers mappés en mémoire pour le disque IO dans une production application? Autrement dit, les applications qui ont des temps de fonctionnement continu avec redémarrages minimum (une fois par mois, maximum). anecdotes de la vie réelle de la production applications (Java ou autre) préféré.

Question n ° 3 est important - je pourrais répondre à cette question moi-même partiellement en écrivant une application « jouet » qui PERF les tests IO en utilisant les différentes options décrites ci-dessus, mais en affichant à SO J'espère pour des anecdotes réelles / données à mâchouiller.

[EDIT] Précision - chaque jour de fonctionnement, l'application crée plusieurs fichiers dont la taille de 100 Mo à 1 Go. Au total, la demande pourrait être en train d'écrire sur plusieurs concerts de données par jour.

Était-ce utile?

La solution

Vous pourriez être en mesure d'accélérer un peu les choses en examinant la façon dont vos données sont tamponnées au cours des écritures. Ceci tend à être application spécifique que vous auriez besoin d'une idée des modèles d'écriture des données attendues. Si la cohérence des données est importante, il y aura des compromis à faire ici.

Si vous êtes en train d'écrire de nouvelles données sur le disque de votre application, la mémoire mappée E / S ne sera probablement pas beaucoup d'aide. Je ne vois aucune raison pour laquelle vous voulez investir du temps dans une solution native code personnalisé. Il semble comme trop complexe pour votre application, de ce que vous avez fourni jusqu'à présent.

Si vous êtes sûr que vous avez vraiment besoin de meilleures performances d'E / S - ou tout simplement la performance O dans votre cas, je regarde dans une solution matérielle comme une matrice de disques à l'écoute. Lancer plus de matériel au problème est souvent plus rentable d'un point de vue commercial que de passer du temps à optimiser le logiciel. Il est également généralement plus rapide à mettre en œuvre et plus fiable.

En général, il y a beaucoup de pièges dans plus de l'optimisation des logiciels. Vous introduire de nouveaux types de problèmes à votre application. Vous pourriez rencontrer des problèmes de mémoire raclée / GC qui conduirait à plus d'entretien / réglage. Le pire est que beaucoup de ces questions seront difficiles à tester avant d'entrer dans la production.

Si elle était mon application, je serais probablement coller avec le FileOutputStream avec une mise en mémoire tampon peut-être à l'écoute. Après que j'utiliser la solution honorée dans le temps de jeter plus de matériel à elle.

Autres conseils

La mémoire mappée E / S ne fera pas vos disques courir plus vite (!). Pour un accès linéaire, il semble un peu inutile.

Un tampon cartographié NIO est la chose réelle (mise en garde habituelle au sujet de toute mise en œuvre raisonnable).

Comme avec d'autres NIO tampons alloués directement, les tampons ne sont pas la mémoire normale et la coutume se GCed aussi efficacement. Si vous créez beaucoup d'entre eux, vous pouvez constater que vous manquez d'espace mémoire / adresse sans manquer de tas Java. Ceci est évidemment un souci avec les processus longs de fonctionnement.

D'après mon expérience, la mémoire des fichiers mis en correspondance sont beaucoup plus performantes que l'accès aux fichiers simple dans en temps réel et les cas d'utilisation de la persistance. Je travaille principalement avec C ++ sous Windows, mais les performances de Linux sont semblables, et que vous avez l'intention d'utiliser JNI de toute façon, donc je pense qu'il applique à votre problème.

Pour un exemple d'un moteur de persistance construit sur fichier mappé en mémoire, voir Metakit . Je l'ai utilisé dans une application où les objets sont de simples vues sur les données mappés en mémoire, le moteur a pris soin de tous les trucs de cartographie derrière les rideaux. Ce fut à la fois rapide et efficace de la mémoire (au moins par rapport aux approches traditionnelles comme celles de la version précédente utilisée), et nous nous sommes engageons / transactions rollback gratuitement.

Dans un autre projet que je devais écrire des applications de réseau de multidiffusion. Les données ont été envoyés dans un ordre aléatoire afin de minimiser l'impact de la perte de paquets consécutifs (combinée à des programmes FEC et de blocage). De plus, les données pourraient bien dépasser l'espace d'adressage (fichiers vidéo étaient plus grandes que 2Gb) si l'allocation de mémoire était hors de question. Du côté du serveur, des sections de fichiers ont été mémoire mappée sur la demande et la couche réseau cueillies directement les données de ces vues; en conséquence, l'utilisation de la mémoire était très faible. Du côté du récepteur, il n'y avait aucun moyen de prédire l'ordre dans lequel les paquets ont été reçus, il doit maintenir un nombre limité de vues actives sur le fichier cible, et les données ont été copiées directement dans ces vues. Lorsqu'un paquet doit être mis dans une zone non cartographiées, la vue la plus ancienne était unmapped (et éventuellement rincée dans le fichier par le système) et remplacé par une nouvelle vue sur la zone de destination. Les performances sont remarquables, notamment parce que le système a fait un excellent travail à commettre des données en tâche de fond, et les contraintes en temps réel ont été facilement satisfaits.

Depuis, je suis convaincu que même le meilleur système de logiciel conçu fin ne peut pas battre par défaut de la politique d'E / S du système avec le fichier mappé en mémoire, parce que le système connaît plus que les applications de l'espace utilisateur sur le moment et la façon dont les données doivent être écrit. En outre, ce qui est important est de savoir que le mappage de mémoire est indispensable lorsqu'il s'agit de grandes quantités de données, car les données ne sont jamais attribués (d'où la mémoire consomme) mais dynamiquement mappée dans l'espace d'adressage, et géré par le gestionnaire de mémoire virtuelle du système, qui est toujours plus vite que le tas. Ainsi, le système utilise toujours la mémoire de manière optimale, et engage des données à chaque fois qu'il doit, derrière le dos de l'application sans impact sur elle.

it helps.

En ce qui concerne le point 3 - si les accidents de la machine et il y a des pages qui ne sont pas vidées sur le disque, ils sont perdus. Une autre chose est le gaspillage de l'espace d'adressage - mapper un fichier dans la mémoire consomme de l'espace d'adressage (et nécessite zone contiguë), et bien, sur les machines 32 bits, il est un peu limité. Mais vous avez dit au sujet de 100MB - donc il ne devrait pas être un problème. Et une chose -. Augmenter la taille du fichier mmaped exige un certain travail

Par ailleurs, cette discussion SO peut aussi vous donner quelques idées.

Je l'ai fait une étude où Je compare les performances d'écriture à une ByteBuffer brute par rapport à la performance d'écriture à un MappedByteBuffer. fichiers mappés en mémoire sont pris en charge par le système d'exploitation et leurs latences d'écriture sont très bonnes que vous pouvez voir dans mes numéros de référence. Exécution d'écriture synchrone par un FileChannel est d'environ 20 fois plus lent et c'est pourquoi les gens font asynchrone l'exploitation forestière tout le temps. Dans mon étude, je donne aussi un exemple de la façon de mettre en œuvre la journalisation asynchrone par une file d'attente sans blocage et sans déchets pour une performance ultime très proche d'une première ByteBuffer.

Si vous écrivez moins d'octets, il sera plus rapide. Que faire si vous filtraient à travers gzipoutputstream, ou si vous avez écrit vos données dans zipfiles ou JarFiles?

Comme mentionné ci-dessus, utilisez NIO (nouveau IO a.k.a.). Il y a aussi une nouvelle, nouvelle IO sortant.

L'utilisation correcte d'une solution de disque dur RAID vous aiderait, mais ce serait une douleur.

Je aime vraiment l'idée de la compression des données. Optez pour le mec gzipoutputstream! Cela doubler votre débit si la CPU peut maintenir. Il est probable que vous pouvez profiter des machines à double cœur maintenant standard, hein?

-Stosh

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