240 votes

mmap() vs lit blocs

Je suis en train de travailler sur un programme de traitement de fichiers pouvant être 100 go ou plus en taille. Les fichiers contiennent des ensembles d'enregistrements de longueur variable. J'ai une première mise en œuvre et en cours d'exécution et je suis maintenant à la recherche pour améliorer les performances, en particulier à faire des I/O de manière plus efficace depuis le fichier d'entrée sera scannée à de nombreuses reprises.

Est-il une règle de pouce pour l'utilisation de mmap() par rapport à la lecture dans les blocs via C++de fstream bibliothèque? Ce que je voudrais faire est de lire les gros pâtés de maisons de disque dans un buffer, les processus, les dossiers complets à partir de la mémoire tampon, puis en lire plus.

Le mmap() code pourrait être potentiellement très salissant depuis mmap avais blocs besoin de mentir sur la page de la taille des frontières (à ma connaissance) et les enregistrements pourraient potentiellement comme à travers les limites de la page. Avec fstreams, je cherche juste le début d'un enregistrement et commencer à lire à nouveau, comme nous ne sommes pas limités à la lecture des blocs qui se trouvent sur la page de la taille des frontières.

Comment puis-je choisir entre ces deux options, sans réellement la rédaction d'une mise en œuvre complète de la première? Toutes les règles de base (par exemple, mmap() est 2x plus rapide) ou de simples tests?

264voto

Dietrich Epp Points 72865

J'ai essayé de trouver le mot de la fin sur mmap / performances de lecture sur Linux et je suis tombé sur un post-nice (lien) sur le noyau Linux liste de diffusion. C'est à partir de 2000, donc il y a eu de nombreuses améliorations à l'interface IO et de la mémoire virtuelle du noyau depuis, mais il explique bien la raison pour laquelle mmap ou read pourrait être plus rapide ou plus lent.

  • Un appel à l' mmap a plus de ressources que l' read (comme epoll a plus de ressources que l' poll, ce qui a plus de ressources que l' read). Évolution des mappages de mémoire virtuelle est une opération coûteuse sur certains processeurs pour les mêmes raisons que la commutation entre les différents processus est coûteux.
  • Le système e / s peut déjà utiliser le cache de disque, donc, si vous lisez un fichier, vous serez frappé de la mémoire cache ou manquer peu importe la méthode que vous utilisez.

Cependant,

  • Cartes mémoire sont généralement plus rapides pour un accès aléatoire, surtout si vos modèles d'accès sont rares et imprévisibles.
  • La mémoire des cartes vous permettent de garder à l'aide de pages à partir du cache jusqu'à ce que vous sont effectuées. Cela signifie que si vous utilisez un fichier lourdement pour une longue période de temps, puis le fermer et le rouvrir, les pages doivent encore être mis en cache. Avec read, votre fichier peut avoir été supprimées du cache il y a des siècles. Cela ne s'applique pas si vous utilisez un fichier et de le jeter immédiatement. (Si vous essayez d' mlock pages juste pour les garder dans le cache, vous êtes en essayant de déjouer le cache disque et ce genre de bêtises rarement aide à la performance du système).
  • La lecture d'un fichier directement est très simple et rapide.

La discussion de mmap/lire me rappelle de deux autres discussions sur le rendement:

  • Certains programmeurs Java ont été choqués de découvrir que non bloquantes I/O est souvent plus lent que le blocage des I/O, ce qui est tout à fait logique si vous savez que non bloquantes I/O nécessite de faire plus d'appels.

  • Un autre réseau programmeurs ont été choqués d'apprendre qu' epoll est souvent plus lent que l' poll, ce qui est logique si vous savez que la gestion de la epoll nécessite de faire plus d'appels.

Conclusion: l'Utilisation de la mémoire des cartes si vous accédez à des données au hasard, le garder pour une longue période de temps, ou si vous savez que vous pouvez le partager avec d'autres processus (MAP_SHARED n'est pas très intéressant si il n'y a pas de partage). Lire les fichiers normalement, si vous accédez à des données de manière séquentielle ou de le rejeter après la lecture. Et si la méthode rend votre programme moins complexe, que. Pour beaucoup de monde réel des cas, il n'est pas sûr de la façon de montrer l'un est plus rapide sans le test de votre application réelle et non PAS un indice de référence.

(Désolé pour le nécro qui pratiquent cette question, mais je cherchais une réponse à cette question a cessé de venir dans le haut des résultats de Google.)

51voto

Tim Cooper Points 2481

Le principal coût de performance va être disk i/o". mmap()" est certainement plus rapide que istream, mais la différence peut ne pas être sensible parce que les i/o disque va dominer votre temps.

J'ai essayé de Ben Collins du fragment de code (voir ci-dessus/ci-dessous), afin de tester son affirmation que "mmap() est la manière la plus rapide" et n'a trouvé aucune différence mesurable. Voir mes commentaires sur sa réponse.

Je ne peux pas recommander séparément mmap avec chaque enregistrement, à son tour, à moins que votre "dossiers" sont énormes - ce serait horriblement lent, nécessitant 2 appels système pour chaque enregistrement et peut-être perdre la page du disque, de la mémoire cache.....

Dans votre cas, je pense que mmap(), istream et le faible niveau de open()/lecture() appels seront sur le même. Je recommande mmap() dans ces cas:

  1. Il est en accès aléatoire (non séquentiel) dans le fichier, ET
  2. le tout s'adapte confortablement dans la mémoire OU il y a de la localité de référence dans le fichier, de sorte que certaines pages peuvent être mappés et les autres pages du tracé. De cette façon, le système d'exploitation utilise la RAM disponible pour un maximum de profit.
  3. OU si plusieurs processus de lecture/travail sur le même fichier mmap() est fantastique parce que les processus partagent tous les mêmes pages physiques.

(btw, j'adore mmap()/MapViewOfFile()).

42voto

Ben Collins Points 11318

mmap est de façon plus rapide. Vous pouvez écrire une simple référence pour le prouver à vous-même:

char data[0x1000];
std::ifstream in("file.bin");

while (in)
{
  in.read(data, 0x1000);
  // do something with data
}

contre:

const int file_size=something;
const int page_size=0x1000;
int off=0;
void *data;

int fd = open("filename.bin", O_RDONLY);

while (off < file_size)
{
  data = mmap(NULL, page_size, PROT_READ, 0, fd, off);
  // do stuff with data
  munmap(data, page_size);
  off += page_size;
}

Clairement, je suis en laissant de côté les détails (comme la façon de déterminer quand vous avez atteint la fin du fichier dans le cas où votre fichier n'est pas un multiple de page_size, par exemple), mais ça ne devrait pas être beaucoup plus compliqué que cela.

Si vous le pouvez, vous pouvez essayer de briser vos données dans plusieurs fichiers qui peuvent être mmap()-ed dans son ensemble plutôt que dans la partie (beaucoup plus simple).

Il y A quelques mois j'ai eu une demi-cuite de la mise en œuvre d'un coulissante de la fenêtre de mmap()-ed classe de flux pour boost_iostreams, mais personne ne se souciait et je me suis occupé avec d'autres choses. Malheureusement, j'ai supprimé des archives de vieux projets non achevés il y a quelques semaines, et qui a été l'une des victimes :-(

Mise à jour: je dois également ajouter l'avertissement que ce test sera assez différent dans Windows parce que Microsoft a mis en place un astucieux fichier cache qui réalise la plupart de ce que vous feriez avec mmap en premier lieu. I. e., souvent d'accéder à des fichiers, vous pouvez simplement faire std::ifstream.read (), et il serait aussi vite que mmap, car le fichier de cache aurais déjà fait une projection en mémoire pour vous, et c'est transparent.

Dernière mise à Jour: Regardez, les gens: à travers un grand nombre de différentes combinaisons de plate-forme de l'OS et de la norme des bibliothèques et des disques et de la mémoire des hiérarchies, je ne peux pas dire à certains que l'appel système mmap, considéré comme une boîte noire, toujours toujours toujours être sensiblement plus rapide que l' read. Ce n'était pas exactement mon intention, même si mes mots pourraient être interprétées de cette façon. En fin de compte, mon point était que memory-mapped i/o est généralement plus rapide que d'octets d'e/s; c'est encore vrai. Si vous trouvez expérimentalement qu'il n'y a pas de différence entre les deux, la seule explication qui me semble raisonnable, c'est que votre plate-forme met en œuvre de carte mémoire sous les couvertures dans une manière qui est avantageux pour la performance d'appels à l' read. La seule façon d'être absolument certain que vous êtes à l'aide de memory-mapped i/o dans un portable est d'utiliser mmap. Si vous n'avez pas de soins sur la portabilité et vous pouvez compter sur les caractéristiques de votre cible plates-formes, puis à l'aide de read peut être adapté sans pour autant sacrifier sensiblement les performances.

7voto

mlbrock Points 61

Je suis désolé, Ben Collins a perdu ses fenêtres coulissantes mmap code source. Ce serait sympa d'avoir de coup de pouce.

Oui, la cartographie du fichier est beaucoup plus rapide. Vous êtes essentiellement en utilisant l'OS de la mémoire virtuelle sous-système d'associer la mémoire à disque et vice versa. Pensez-y de cette façon: si le noyau de système d'exploitation que les développeurs pourraient le rendre plus rapide qu'ils le feraient. Parce que cela fait à peu près tout plus vite: bases de données, le temps de démarrage, le programme de temps de chargement, et cetera.

La fenêtre coulissante approche n'est vraiment pas si difficile que plusieurs continguous pages peuvent être mappées à la fois. De sorte que la taille de l'enregistrement n'a pas d'importance tant que le plus grand de tout enregistrement unique, à tenir dans la mémoire. L'important, c'est la gestion de la comptabilité.

Si un enregistrement ne commence pas sur une getpagesize() limite, votre cartographie doit commencer à la page précédente. La longueur de la région cartographiée s'étend à partir du premier octet de l'enregistrement (arrondi vers le bas, si nécessaire, au plus proche multiple de getpagesize()) pour le dernier octet de l'enregistrement (arrondi au multiple le plus proche de getpagesize()). Lorsque vous avez terminé le traitement d'un dossier, vous pouvez annuler le mappage (), et de passer à la suivante.

Tout cela fonctionne très bien sous Windows à l'aide de CreateFileMapping() et MapViewOfFile() (et GetSystemInfo() pour obtenir SYSTEM_INFO.dwAllocationGranularity --- pas SYSTEM_INFO.dwPageSize).

4voto

Ben Collins Points 11318

@jbl:

la fenêtre coulissante mmap sons intéressant. Pouvez-vous en dire un peu plus à ce sujet?

Assurez-vous - j'ai écrit une bibliothèque C++ pour Git (un libgit++, si vous voulez), et j'ai rencontré un problème similaire à ceci: j'ai besoin d'être en mesure d'ouvrir large (très large) des fichiers et de ne pas avoir le rendement total de chien (comme elle le serait avec std::fstream).

Boost::Iostreams a déjà un mapped_file Source, mais le problème était qu'il était en mmapping ensemble de fichiers, ce qui vous limite à 2^(wordsize). Sur les machines 32 bits, 4 GO n'est pas assez grand. Il n'est pas déraisonnable de s'attendre à avoir d' .pack fichiers dans Git, qui deviennent beaucoup plus que cela, j'ai donc besoin de lire le fichier en morceaux sans recourir à régulièrement des e/s de fichier. Sous les couvertures de Boost::Iostreams, j'ai mis en place une Source, qui est plus ou moins un autre point de vue de l'interaction entre l' std::streambuf et std::istream. Vous pouvez également essayer une approche semblable en tout juste d'hériter std::filebuf en mapped_filebuf et, de même, héritant std::fstream en a mapped_fstream. C'est l'interaction entre les deux c'est difficile d'obtenir le droit. Boost::Iostreams a une partie du travail fait pour vous, et il fournit également des crochets pour les filtres et les chaînes, j'ai donc pensé qu'il serait plus utile de mettre en œuvre cette manière.

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