Je travaille sur un programme qui traitera des fichiers dont la taille pourrait potentiellement atteindre 100 Go ou plus. Les fichiers contiennent des ensembles d'enregistrements de longueur variable. J'ai une première implémentation opérationnelle et je cherche maintenant à améliorer les performances, en particulier à faire des E / S plus efficacement puisque le fichier d'entrée est analysé plusieurs fois.
Existe-t-il une règle de base pour utiliser mmap()
ou lire dans des blocs via la fstream
bibliothèque C ++ ? Ce que j'aimerais faire, c'est lire de gros blocs du disque dans une mémoire tampon, traiter des enregistrements complets à partir de la mémoire tampon, puis en savoir plus.
Le mmap()
code pourrait potentiellement devenir très compliqué car mmap
les blocs 'd doivent se trouver sur les limites de la taille de la page (à ma connaissance) et les enregistrements pourraient potentiellement passer à travers les limites de la page. Avec fstream
s, je peux simplement chercher le début d'un enregistrement et recommencer la lecture, car nous ne sommes pas limités à la lecture de blocs qui se trouvent sur des limites de la taille d'une page.
Comment puis-je choisir entre ces deux options sans rédiger d'abord une implémentation complète? Des règles empiriques (par exemple, mmap()
est-ce 2x plus rapide) ou des tests simples?
mmap()
c'est 2 à 6 fois plus rapide que d'utiliser des appels système, par exempleread()
.Réponses:
J'essayais de trouver le dernier mot sur les performances mmap / read sous Linux et je suis tombé sur un joli post ( lien ) sur la liste de diffusion du noyau Linux. Il date de 2000, il y a donc eu de nombreuses améliorations aux E / S et à la mémoire virtuelle dans le noyau depuis lors, mais cela explique bien la raison pour laquelle
mmap
ouread
pourrait être plus rapide ou plus lent.mmap
a plus de frais généraux queread
(tout commeepoll
a plus de frais généraux quepoll
, qui a plus de frais généraux queread
). La modification des mappages de mémoire virtuelle est une opération assez coûteuse sur certains processeurs pour les mêmes raisons que la commutation entre différents processus est coûteuse.cependant,
read
, votre fichier a peut-être été vidé du cache il y a longtemps. Cela ne s'applique pas si vous utilisez un fichier et que vous le supprimez immédiatement. (Si vous essayez demlock
pages juste pour les garder dans le cache, vous essayez de déjouer le cache disque et ce genre de sottise aide rarement les performances du système).La discussion sur mmap / read me rappelle deux autres discussions sur les performances:
Certains programmeurs Java ont été choqués de découvrir que les E / S non bloquantes sont souvent plus lentes que les E / S bloquantes, ce qui est parfaitement logique si vous savez que les E / S non bloquantes nécessitent plus d'appels système.
Certains autres programmeurs de réseau ont été choqués d'apprendre que
epoll
c'est souvent plus lent quepoll
, ce qui est parfaitement logique si vous savez que la gestionepoll
nécessite de faire plus d'appels système.Conclusion: utilisez des cartes mémoire si vous accédez aux données de manière aléatoire, que vous les gardez longtemps, ou si vous savez que vous pouvez les partager avec d'autres processus (ce
MAP_SHARED
n'est pas très intéressant s'il n'y a pas de partage réel). Lisez les fichiers normalement si vous accédez aux données de manière séquentielle ou supprimez-les après la lecture. Et si l'une ou l'autre méthode rend votre programme moins complexe, faites -le . Pour de nombreux cas du monde réel, il n'y a pas de moyen sûr de montrer que l'un est plus rapide sans tester votre application réelle et PAS une référence.(Désolé d'avoir nécrosé cette question, mais je cherchais une réponse et cette question revenait sans cesse en haut des résultats Google.)
la source
mmap
vsread()
dans ce thread soient toujours vrais comme dans le passé, les performances globales ne peuvent pas vraiment être déterminées en additionnant les avantages et les inconvénients, mais uniquement en testant une configuration matérielle particulière. Par exemple, il est discutable que "Un appel à mmap a plus de surcharge que de lecture" - ouimmap
doit ajouter des mappages à la table de page de processus, maisread
doit copier tous les octets lus du noyau vers l'espace utilisateur.mmap
la surcharge est inférieure à celleread
des lectures plus grandes que la taille d'une page (4 Kio). Maintenant, il est très vrai que si vous voulez accéder aux données de manière clairsemée et aléatoire,mmap
c'est vraiment très bien - mais l'inverse n'est pas nécessaire:mmap
peut-être toujours le meilleur pour un accès séquentiel.mmap
plus rapide, je m'attendrais à voir au minimum tout l'appareil de test (code source) avec les résultats tabulés et le numéro de modèle du processeur.mmap
cela ne vide pas le TLB, sauf dans des circonstances inhabituelles (maismunmap
pourraient). Mes tests incluaient à la fois des microbenchmarks (y comprismunmap
) et aussi "in application" s'exécutant dans un cas d'utilisation réel. Bien sûr, mon application n'est pas la même que votre application, donc les gens devraient tester localement. Il n'est même pas clair que cemmap
soit favorisé par un micro-benchmark:read()
obtient également un gros coup de pouce puisque le tampon de destination côté utilisateur reste généralement en L1, ce qui peut ne pas se produire dans une application plus grande. Alors oui, "c'est compliqué".Le principal coût de performance sera les E / S de disque. "mmap ()" est certainement plus rapide que istream, mais la différence n'est peut-être pas perceptible car les entrées / sorties de disque domineront vos temps d'exécution.
J'ai essayé le fragment de code de Ben Collins (voir ci-dessus / ci-dessous) pour tester son affirmation selon laquelle "mmap () est beaucoup plus rapide" et je n'ai trouvé aucune différence mesurable. Voir mes commentaires sur sa réponse.
Je ne recommanderais certainement pas de mmaping séparément chaque enregistrement à son tour à moins que vos «enregistrements» ne soient énormes - ce serait horriblement lent, nécessitant 2 appels système pour chaque enregistrement et peut-être perdre la page du cache de la mémoire disque .... .
Dans votre cas, je pense que mmap (), istream et les appels open () / read () de bas niveau seront tous à peu près identiques. Je recommanderais mmap () dans ces cas:
(btw - J'adore mmap () / MapViewOfFile ()).
la source
mmap est bien plus rapide. Vous pourriez écrire un simple benchmark pour vous le prouver:
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 laisse de côté des détails (comme comment déterminer quand vous atteignez la fin du fichier dans le cas où votre fichier n'est pas un multiple de
page_size
, par exemple), mais cela ne devrait vraiment pas être beaucoup plus compliqué que cela .Si vous le pouvez, vous pouvez essayer de diviser vos données en plusieurs fichiers qui peuvent être mmap () - édités en totalité plutôt qu'en partie (beaucoup plus simple).
Il y a quelques mois, j'avais une implémentation à moitié cuite d'une classe de flux mmap () à fenêtre coulissante pour boost_iostreams, mais personne ne s'en souciait et je me suis occupé d'autres choses. Malheureusement, j'ai supprimé une archive d'anciens projets inachevés il y a quelques semaines, et c'était l'une des victimes :-(
Mise à jour : je devrais également ajouter la mise en garde que ce benchmark serait assez différent dans Windows car Microsoft a implémenté un cache de fichiers astucieux qui fait la plupart de ce que vous feriez avec mmap en premier lieu. Par exemple, pour les fichiers fréquemment consultés, vous pouvez simplement faire std :: ifstream.read () et ce serait aussi rapide que mmap, car le cache de fichiers aurait déjà fait un mappage mémoire pour vous, et il est transparent.
Dernière mise à jour : Écoutez, les gens: sur de nombreuses combinaisons de plates-formes différentes de systèmes d'exploitation et de bibliothèques standard, de disques et de hiérarchies de mémoire, je ne peux pas dire avec certitude que l'appel système
mmap
, considéré comme une boîte noire, sera toujours toujours beaucoup plus rapide queread
. Ce n'était pas exactement mon intention, même si mes paroles pouvaient être interprétées de cette façon. En fin de compte, mon point était que les E / S mappées en mémoire sont généralement plus rapides que les E / S basées sur octets; c'est toujours vrai . Si vous constatez expérimentalement qu'il n'y a pas de différence entre les deux, alors la seule explication qui me semble raisonnable est que votre plate-forme implémente le mappage mémoire sous les couvertures d'une manière qui est avantageuse pour la performance des appels àread
. La seule façon d'être absolument certain que vous utilisez des E / S mappées en mémoire de manière portable est d'utilisermmap
. Si vous ne vous souciez pas de la portabilité et que vous pouvez vous fier aux caractéristiques particulières de vos plates-formes cibles, l'utilisationread
peut être appropriée sans sacrifier de manière mesurable les performances.Modifier pour nettoyer la liste de réponses: @jbl:
Bien sûr - J'écrivais une bibliothèque C ++ pour Git (une libgit ++, si vous voulez), et j'ai rencontré un problème similaire à celui-ci: je devais être capable d'ouvrir de gros (très gros) fichiers et de ne pas avoir les performances comme un chien total (comme ce serait avec
std::fstream
).Boost::Iostreams
a déjà une source mapped_file, mais le problème était qu'il envoyait unmmap
ping à des fichiers entiers, ce qui vous limite à 2 ^ (taille des mots). Sur les machines 32 bits, 4 Go ne sont pas assez grands. Il n'est pas déraisonnable de s'attendre à avoir des.pack
fichiers dans Git qui deviennent beaucoup plus volumineux que cela, j'ai donc eu besoin de lire le fichier en morceaux sans recourir à des entrées / sorties de fichiers régulières. Sous les couvertures deBoost::Iostreams
, j'ai implémenté une Source, qui est plus ou moins une autre vision de l'interaction entrestd::streambuf
etstd::istream
. Vous pouvez également essayer une approche similaire en héritant justestd::filebuf
enmapped_filebuf
et de même, héritantstd::fstream
ena mapped_fstream
. C'est l'interaction entre les deux qui est difficile à obtenir.Boost::Iostreams
a une partie du travail fait pour vous, et il fournit également des crochets pour les filtres et les chaînes, donc j'ai pensé qu'il serait plus utile de l'implémenter de cette façon.la source
mmap()
du fichier une page à la fois? Si asize_t
est suffisamment volumineux pour contenir la taille du fichier (très probablement sur les systèmes 64 bits), alors justemmap()
le fichier entier en un seul appel.Il y a déjà beaucoup de bonnes réponses ici qui couvrent de nombreux points saillants, donc je vais juste ajouter quelques problèmes que je n'ai pas vu abordés directement ci-dessus. Autrement dit, cette réponse ne doit pas être considérée comme un ensemble des avantages et des inconvénients, mais plutôt comme un addendum à d'autres réponses ici.
mmap semble magique
En prenant le cas où le fichier est déjà entièrement mises en cache 1 comme la ligne de base 2 ,
mmap
peut sembler un peu comme la magie :mmap
ne nécessite qu'un seul appel système pour (potentiellement) mapper le fichier entier, après quoi aucun autre appel système n'est nécessaire.mmap
ne nécessite pas de copie des données du fichier du noyau vers l'espace utilisateur.mmap
vous permet d'accéder au fichier "en tant que mémoire", y compris en le traitant avec toutes les astuces avancées que vous pouvez faire contre la mémoire, telles que la vectorisation automatique du compilateur, les intrinsèques SIMD , la prélecture, les routines d'analyse optimisées en mémoire, OpenMP, etc.Dans le cas où le fichier est déjà dans le cache, cela semble impossible à battre: vous accédez directement au cache des pages du noyau en tant que mémoire et cela ne peut pas aller plus vite que cela.
Eh bien, c'est possible.
mmap n'est pas vraiment magique parce que ...
mmap fonctionne toujours par page
Un coût caché principal de
mmap
vsread(2)
(qui est vraiment l'appel système comparable au niveau du système d'exploitation pour la lecture de blocs ) est quemmap
vous devrez faire "un peu de travail" pour chaque page 4K dans l'espace utilisateur, même si elle peut être masquée par le mécanisme d'erreur de page.Par exemple, une implémentation typique qui ne
mmap
contient que le fichier entier devra être défaillante, donc 100 Go / 4K = 25 millions de défauts pour lire un fichier de 100 Go. Maintenant, ce seront des défauts mineurs , mais 25 milliards de défauts de page ne seront toujours pas très rapides. Le coût d'une faute mineure est probablement de l'ordre de 100 nanos dans le meilleur des cas.mmap s'appuie fortement sur les performances TLB
Maintenant, vous pouvez passer
MAP_POPULATE
àmmap
pour lui dire de configurer toutes les tables de pages avant de revenir, il ne devrait donc y avoir aucun défaut de page lors de l'accès. Maintenant, cela a le petit problème qu'il lit également le fichier entier dans la RAM, ce qui va exploser si vous essayez de mapper un fichier de 100 Go - mais ignorons cela pour l'instant 3 . Le noyau doit effectuer un travail par page pour configurer ces tables de pages (apparaît comme l'heure du noyau). Cela finit par être un coût majeur dans l'mmap
approche, et il est proportionnel à la taille du fichier (c'est-à-dire qu'il ne devient pas relativement moins important à mesure que la taille du fichier augmente) 4 .Enfin, même dans l'espace utilisateur, l'accès à un tel mappage n'est pas exactement gratuit (par rapport aux grands tampons de mémoire ne provenant pas d'un fichier
mmap
) - même une fois que les tables de pages sont configurées, chaque accès à une nouvelle page va, conceptuellement, encourir un échec TLB. Étant donnémmap
qu'introduire un fichier signifie utiliser le cache de pages et ses pages 4K, vous engagez à nouveau ce coût 25 millions de fois pour un fichier de 100 Go.Désormais, le coût réel de ces erreurs TLB dépend au moins des aspects suivants de votre matériel: (a) combien d'entrées 4K TLB vous avez et comment le reste de la mise en cache de traduction fonctionne (b) la qualité de la prélecture matérielle avec le TLB - par exemple, la prélecture peut-elle déclencher un parcours de page? (c) à quelle vitesse et à quel point le matériel de marche de page est parallèle. Sur les processeurs Intel x86 haut de gamme modernes, le matériel de marche de page est en général très solide: il y a au moins 2 marcheurs de page parallèles, une marche de page peut se produire en même temps que l'exécution continue et la prélecture matérielle peut déclencher une marche de page. Ainsi, l'impact du TLB sur une charge de lecture en continu est assez faible - et une telle charge fonctionnera souvent de la même manière quelle que soit la taille de la page. Cependant, les autres matériels sont généralement bien pires!
read () évite ces pièges
L'
read()
appel syscall, qui sous-tend généralement les appels de type "lecture de bloc" proposés, par exemple en C, C ++ et dans d'autres langages, présente un inconvénient majeur dont tout le monde est bien conscient:read()
appel de N octets doit copier N octets du noyau vers l'espace utilisateur.D'un autre côté, cela évite la plupart des coûts ci-dessus - vous n'avez pas besoin de mapper 25 millions de pages 4K dans l'espace utilisateur. Vous pouvez généralement
malloc
utiliser un seul petit tampon dans l'espace utilisateur et le réutiliser à plusieurs reprises pour tous vosread
appels. Du côté du noyau, il n'y a presque aucun problème avec les pages 4K ou les ratés TLB car toute la RAM est généralement mappée de manière linéaire en utilisant quelques très grandes pages (par exemple, des pages de 1 Go sur x86), de sorte que les pages sous-jacentes du cache de page sont couvertes très efficacement dans l'espace noyau.Donc, en gros, vous avez la comparaison suivante pour déterminer laquelle est la plus rapide pour une seule lecture d'un gros fichier:
Le travail supplémentaire par page impliqué par l'
mmap
approche est-il plus coûteux que le travail par octet de copie du contenu du fichier du noyau vers l'espace utilisateur impliqué par l'utilisationread()
?Sur de nombreux systèmes, ils sont en fait à peu près équilibrés. Notez que chacun évolue avec des attributs complètement différents du matériel et de la pile du système d'exploitation.
En particulier, l'
mmap
approche devient relativement plus rapide lorsque:MAP_POPULATE
implémentation qui peut traiter efficacement de grandes cartes dans les cas où, par exemple, les pages sous-jacentes sont contiguës dans la mémoire physique.... tandis que l'
read()
approche devient relativement plus rapide lorsque:read()
appel système a de bonnes performances de copie. Par exemple, de bonnescopy_to_user
performances côté noyau.Les facteurs matériels ci-dessus varient énormément selon les plates-formes, même au sein de la même famille (par exemple, au sein des générations x86 et en particulier des segments de marché) et certainement d'une architecture à l'autre (par exemple, ARM vs x86 vs PPC).
Les facteurs du système d'exploitation changent également, avec diverses améliorations des deux côtés provoquant un grand saut de la vitesse relative pour une approche ou l'autre. Une liste récente comprend:
mmap
cas sansMAP_POPULATE
.copy_to_user
méthodes rapides dansarch/x86/lib/copy_user_64.S
, par exemple, en utilisantREP MOVQ
quand il est rapide, ce qui aide vraiment leread()
cas.Mise à jour après Spectre et Meltdown
Les atténuations des vulnérabilités Spectre et Meltdown ont considérablement augmenté le coût d'un appel système. Sur les systèmes que j'ai mesurés, le coût d'un appel système "ne rien faire" (qui est une estimation de la surcharge pure de l'appel système, en dehors de tout travail réel effectué par l'appel) est passé d'environ 100 ns sur un système Linux moderne à environ 700 ns. En outre, en fonction de votre système, le correctif d' isolement de table de page spécifiquement pour Meltdown peut avoir des effets en aval supplémentaires en dehors du coût d'appel système direct en raison de la nécessité de recharger les entrées TLB.
Tout ceci est un inconvénient relatif pour les
read()
méthodes basées par rapport auxmmap
méthodes basées, puisque lesread()
méthodes doivent faire un appel système pour chaque valeur de «taille de tampon» de données. Vous ne pouvez pas augmenter arbitrairement la taille de la mémoire tampon pour amortir ce coût, car l'utilisation de tampons volumineux fonctionne généralement moins bien puisque vous dépassez la taille L1 et que vous souffrez donc constamment d'erreurs de cache.D'autre part, avec
mmap
, vous pouvez mapper dans une grande région de mémoire avecMAP_POPULATE
et y accéder efficacement, au prix d'un seul appel système.1 Cela inclut plus ou moins également le cas où le fichier n'était pas entièrement mis en cache au départ, mais où la lecture anticipée du système d'exploitation est suffisamment bonne pour le faire apparaître ainsi (c'est-à-dire que la page est généralement mise en cache au moment où vous le veux). Ceci est une question subtile mais parce que la façon dont fonctionne la lecture anticipée est souvent tout à fait différente entre
mmap
etread
appels, et peuvent être ajustés par des appels « conseiller les » comme décrit dans 2 .2 ... parce que si le fichier n'est pas mis en cache, votre comportement sera complètement dominé par des problèmes d'E / S, y compris à quel point votre modèle d'accès est sympathique au matériel sous-jacent - et tous vos efforts devraient être pour garantir qu'un tel accès est aussi sympathique que possible, par exemple via l'utilisation de
madvise
ou desfadvise
appels (et quels que soient les changements de niveau d'application que vous pouvez apporter pour améliorer les modèles d'accès).3 Vous pouvez contourner cela, par exemple, en insérant séquentiellement
mmap
dans des fenêtres de plus petite taille, par exemple 100 Mo.4 En fait, il s'avère que l'
MAP_POPULATE
approche (au moins une combinaison matérielle / système d'exploitation) est légèrement plus rapide que de ne pas l'utiliser, probablement parce que le noyau utilise la solution de panne - le nombre réel de défauts mineurs est donc réduit d'un facteur 16 ou alors.la source
mmap
cela aura un avantage insurmontable car cela évite la surcharge fixe des appels du noyau. D'autre part,mmap
augmente également la pression TLB et ralentit en fait la phase de "préchauffage" où les octets sont lus pour la première fois dans le processus en cours (bien qu'ils soient toujours dans la page de page), car cela peut le faire plus de travail queread
, par exemple, pour "contourner les défauts" des pages adjacentes ... et pour les mêmes applications, "réchauffer" est tout ce qui compte! @CaetanoSauerJe suis désolé que Ben Collins ait perdu le code source de sa fenêtre coulissante mmap. Ce serait bien d'avoir dans Boost.
Oui, le mappage du fichier est beaucoup plus rapide. Vous utilisez essentiellement le sous-système de mémoire virtuelle du système d'exploitation pour associer la mémoire au disque et vice versa. Pensez-y de cette façon: si les développeurs du noyau du système d'exploitation pouvaient le rendre plus rapide, ils le feraient. Parce que cela accélère à peu près tout: bases de données, temps de démarrage, temps de chargement du programme, et cetera.
L'approche de la fenêtre coulissante n'est vraiment pas si difficile car plusieurs pages contingentes peuvent être mappées à la fois. Ainsi, la taille de l'enregistrement n'a pas d'importance tant que le plus grand de n'importe quel enregistrement tient dans la mémoire. L'important est de gérer la comptabilité.
Si un enregistrement ne commence pas sur une limite getpagesize (), votre mappage doit commencer sur la page précédente. La longueur de la région mappée s'étend du premier octet de l'enregistrement (arrondi si nécessaire au multiple inférieur de getpagesize ()) jusqu'au dernier octet de l'enregistrement (arrondi au multiple supérieur de getpagesize ()). Lorsque vous avez terminé de traiter un enregistrement, vous pouvez le démapper () et passer au suivant.
Tout cela fonctionne très bien sous Windows également en utilisant CreateFileMapping () et MapViewOfFile () (et GetSystemInfo () pour obtenir SYSTEM_INFO.dwAllocationGranularity --- pas SYSTEM_INFO.dwPageSize).
la source
mmap devrait être plus rapide, mais je ne sais pas combien. Cela dépend beaucoup de votre code. Si vous utilisez mmap, il est préférable de mmapper tout le fichier en une seule fois, cela vous facilitera la vie. Un problème potentiel est que si votre fichier est supérieur à 4 Go (ou en pratique, la limite est inférieure, souvent 2 Go), vous aurez besoin d'une architecture 64 bits. Donc, si vous utilisez un environnement 32, vous ne voudrez probablement pas l'utiliser.
Cela dit, il existe peut-être un meilleur moyen d'améliorer les performances. Vous avez dit que le fichier d'entrée est analysé plusieurs fois , si vous pouvez le lire en un seul passage et en finir avec lui, cela pourrait potentiellement être beaucoup plus rapide.
la source
Vous devriez peut-être pré-traiter les fichiers, de sorte que chaque enregistrement soit dans un fichier séparé (ou au moins que chaque fichier ait une taille mmapable).
Pourriez-vous également effectuer toutes les étapes de traitement pour chaque enregistrement, avant de passer au suivant? Peut-être que cela éviterait une partie des frais généraux d'E / S?
la source
Je conviens que les E / S de fichier mmap'd seront plus rapides, mais pendant que vous comparez le code, le contre-exemple ne devrait-il pas être quelque peu optimisé?
Ben Collins a écrit:
char data[0x1000]; std::ifstream in("file.bin"); while (in) { in.read(data, 0x1000); // do something with data }
Je suggérerais également d'essayer:
char data[0x1000]; std::ifstream iifle( "file.bin"); std::istream in( ifile.rdbuf() ); while( in ) { in.read( data, 0x1000); // do something with data }
Et au-delà de cela, vous pouvez également essayer de faire en sorte que la taille du tampon soit de la même taille qu'une page de mémoire virtuelle, au cas où 0x1000 ne serait pas la taille d'une page de mémoire virtuelle sur votre machine ... IMHO mmap'd file I / O still gagne, mais cela devrait rapprocher les choses.
la source
À mon avis, utiliser mmap () "juste" soulage le développeur d'avoir à écrire son propre code de mise en cache. Dans un simple cas de "lecture rapide du fichier une fois", cela ne sera pas difficile (bien que, comme le souligne mlbrock, vous enregistrez toujours la copie mémoire dans l'espace de processus), mais si vous faites des allers-retours dans le fichier ou en sautant des bits et ainsi de suite, je pense que les développeurs du noyau ont probablement fait un meilleur travail d'implémentation de la mise en cache que moi ...
la source
mmap
mise en cache est que vous réutilisez simplement le cache de page existant qui sera déjà là, vous obtenez donc cette mémoire gratuitement, et elle peut également être partagée entre les processus.Je me souviens avoir mappé un énorme fichier contenant une structure arborescente dans la mémoire il y a des années. J'ai été étonné par la vitesse par rapport à la désérialisation normale qui implique beaucoup de travail en mémoire, comme l'allocation de nœuds d'arbre et la définition de pointeurs. Donc, en fait, je comparais un seul appel à mmap (ou son homologue sous Windows) à de nombreux appels (BEAUCOUP) aux appels d'opérateurs et de constructeurs. Pour ce type de tâche, mmap est imbattable par rapport à la désérialisation. Bien sûr, il faut se pencher sur le pointeur relocalisable des boosts pour cela.
la source
Cela semble être un bon cas d'utilisation pour le multi-threading ... Je pense que vous pourriez assez facilement configurer un thread pour qu'il lise des données pendant que les autres les traitent. Cela peut être un moyen d'augmenter considérablement la performance perçue. Juste une pensée.
la source
Je pense que la meilleure chose à propos de mmap est le potentiel de lecture asynchrone avec:
addr1 = NULL; while( size_left > 0 ) { r = min(MMAP_SIZE, size_left); addr2 = mmap(NULL, r, PROT_READ, MAP_FLAGS, 0, pos); if (addr1 != NULL) { /* process mmap from prev cycle */ feed_data(ctx, addr1, MMAP_SIZE); munmap(addr1, MMAP_SIZE); } addr1 = addr2; size_left -= r; pos += r; } feed_data(ctx, addr1, r); munmap(addr1, r);
Le problème est que je ne trouve pas le bon MAP_FLAGS pour donner un indice que cette mémoire doit être synchronisée à partir du fichier dès que possible. J'espère que MAP_POPULATE donne le bon indice pour mmap (c'est-à-dire qu'il n'essaiera pas de charger tout le contenu avant le retour de l'appel, mais le fera en async. Avec feed_data). Au moins, il donne de meilleurs résultats avec cet indicateur, même si le manuel déclare qu'il ne fait rien sans MAP_PRIVATE depuis 2.6.23.
la source
posix_madvise
avec leWILLNEED
drapeau pour les conseils paresseux à préremplir.posix_madvise
s'agit d'un appel asynchrone. Ce serait également une bonne référencemlock
pour ceux qui veulent attendre que toute la région de la mémoire devienne disponible sans défauts de page.