Quels sont les avantages des fichiers mappés en mémoire?

89

J'ai recherché des fichiers mappés en mémoire pour un projet et j'apprécierais les réflexions de personnes qui les ont déjà utilisées ou qui ont décidé de ne pas les utiliser, et pourquoi?

En particulier, je suis préoccupé par ce qui suit, par ordre d'importance:

  • concurrence
  • accès aléatoire
  • performance
  • facilité d'utilisation
  • portabilité
Robottobor
la source

Réponses:

56

Je pense que l'avantage est vraiment que vous réduisez la quantité de copie de données requise par rapport aux méthodes traditionnelles de lecture d'un fichier.

Si votre application peut utiliser les données «en place» dans un fichier mappé en mémoire, elle peut entrer sans être copiée; si vous utilisez un appel système (par exemple, pread () de Linux), cela implique généralement que le noyau copie les données de ses propres tampons dans l'espace utilisateur. Cette copie supplémentaire prend non seulement du temps, mais diminue l'efficacité des caches du processeur en accédant à cette copie supplémentaire des données.

Si les données doivent réellement être lues à partir du disque (comme dans les E / S physiques), alors le système d'exploitation doit toujours les lire, une erreur de page n'est probablement pas meilleure en termes de performances qu'un appel système, mais si elles pas (c'est-à-dire déjà dans le cache du système d'exploitation), les performances devraient en théorie être bien meilleures.

En revanche, il n'y a pas d'interface asynchrone pour les fichiers mappés en mémoire - si vous essayez d'accéder à une page qui n'est pas mappée, cela génère une erreur de page, puis fait attendre le thread pour les E / S.


L'inconvénient évident des fichiers mappés en mémoire est sur un système d'exploitation 32 bits - vous pouvez facilement manquer d'espace d'adressage.

MarkR
la source
4
Sur Windows au moins, vous pouvez mapper plusieurs vues 32 bits d'un fichier mmap plus grand - ce qui peut être plus efficace que d'essayer de traiter de très gros fichiers à l'aide de la fonction CRT normale
Martin Beckett
@MarkR Vous avez écrit "sa copie supplémentaire prend non seulement du temps, mais diminue l'efficacité des caches du CPU en accédant à cette copie supplémentaire des données. ". (c'est moi qui souligne ). Pouvez-vous expliquer comment la copie de tampon supplémentaire dans le noyau nuit à l'efficacité des caches du processeur?
Geek
4
@Geek accède à deux fois plus de mémoire = deux fois plus de cache gaspillé (très approximativement).
user253751
49

J'ai utilisé un fichier mappé en mémoire pour implémenter une fonction de «saisie semi-automatique» pendant que l'utilisateur tape. J'ai plus d'un million de références de produits stockées dans un seul fichier d'index. Le fichier contient des informations d'en-tête typiques, mais la majeure partie du fichier est un tableau géant d'enregistrements de taille fixe triés sur le champ clé.

Au moment de l'exécution, le fichier est mappé en mémoire, converti en un tableau de Cstyle struct, et nous effectuons une recherche binaire pour trouver les numéros de pièce correspondants lorsque l'utilisateur tape. Seules quelques pages de mémoire du fichier sont réellement lues à partir du disque - quelles que soient les pages touchées lors de la recherche binaire.

  • Concurrence - J'ai eu un problème d'implémentation où il mappait parfois en mémoire le fichier plusieurs fois dans le même espace de processus. C'était un problème si je me souviens bien parce que parfois le système ne pouvait pas trouver un bloc de mémoire virtuelle suffisamment grand pour mapper le fichier. La solution consistait à ne mapper le fichier qu'une seule fois et à tous les appels. Rétrospectivement, l'utilisation d'un service Windows complet aurait été cool.
  • Accès aléatoire - La recherche binaire est certainement un accès aléatoire et rapide comme l'éclair
  • Performance - La recherche est extrêmement rapide. Au fur et à mesure que les utilisateurs saisissent une fenêtre contextuelle affiche une liste des numéros de référence de produits correspondants, la liste se réduit à mesure qu'ils continuent à taper. Il n'y a pas de décalage notable lors de la frappe.
Brian Ensink
la source
1
La recherche binaire ne serait-elle pas lente à mesure que les pages sont lues à chaque tentative? Ou le système d'exploitation est-il suffisamment intelligent pour gérer cela de manière efficace?
jjxtra
1
Je suppose que l'utilisation d'E / S mappées en mémoire est une sorte de gaspillage pour la recherche binaire, car la recherche n'accédera qu'à quelques clés uniques dans des emplacements mémoire relativement éloignés, mais le système d'exploitation se chargera en 4k pages pour chaque demande de ce type. Mais là encore, le fichier avec des parties ne change pas beaucoup, donc le cache aide à couvrir cela. Mais à proprement parler, je crois que la recherche / lecture traditionnelle serait meilleure ici. Enfin, 1 mil, ce n'est pas beaucoup de nos jours. Pourquoi ne pas tout garder en RAM?
le porc
5
@the swine et PsychoDad ma réponse originale était de 2008 et la mise en œuvre réelle de cette fonction de saisie semi-automatique en mémoire était d'environ 2004-2005. Consommer de 800 à 1000 Mo de mémoire physique pour charger l'intégralité du fichier n'était pas une bonne solution pour notre base d'utilisateurs. La solution mappée en mémoire était très rapide et efficace. Ça a frappé et je m'en souviens avec émotion depuis mes débuts en tant que développeur junior. :)
Brian Ensink
@BrianEnsink: ok, c'est logique. Je ne m'attendais pas à ce que chaque entrée atteigne 1 Ko. alors bien sûr l'approche paginée devient plus efficace. nice :)
le porc
22

Les fichiers mappés en mémoire peuvent être utilisés pour remplacer l'accès en lecture / écriture ou pour prendre en charge le partage simultané. Lorsque vous les utilisez pour un mécanisme, vous obtenez également l'autre.

Plutôt que de rechercher, d'écrire et de lire dans un fichier, vous le mappez en mémoire et accédez simplement aux bits où vous vous attendez à ce qu'ils soient.

Cela peut être très pratique et, selon l'interface de la mémoire virtuelle, améliorer les performances. L’amélioration des performances peut se produire car le système d’exploitation gère désormais cette ancienne «E / S de fichier» ainsi que tous vos autres accès à la mémoire par programmation, et peut (en théorie) exploiter les algorithmes de pagination et ainsi de suite qu’il utilise déjà pour prendre en charge mémoire virtuelle pour le reste de votre programme. Cela dépend cependant de la qualité de votre système de mémoire virtuelle sous-jacent. Des anecdotes que j'ai entendues dire que les systèmes de mémoire virtuelle Solaris et * BSD peuvent montrer de meilleures améliorations de performances que le système VM de Linux - mais je n'ai pas de données empiriques pour étayer cela. YMMV.

La concurrence entre en jeu lorsque vous considérez la possibilité que plusieurs processus utilisent le même «fichier» via la mémoire mappée. Dans le modèle de lecture / écriture, si deux processus écrivaient dans la même zone du fichier, vous pourriez être à peu près assuré que l'une des données du processus arriverait dans le fichier, écrasant les données de l'autre processus. Vous obtiendrez l'un ou l'autre - mais pas un mélange étrange. Je dois admettre que je ne suis pas sûr qu'il s'agisse d'un comportement imposé par une norme, mais c'est quelque chose sur lequel vous pouvez compter. (C'est en fait une bonne question de suivi!)

Dans le monde cartographié, en revanche, imaginez deux processus à la fois «d'écriture». Ils le font en effectuant des «magasins de mémoire», ce qui entraîne la pagination des données sur le disque par le système d'exploitation - éventuellement. Mais en attendant, on peut s'attendre à ce que des écritures se chevauchent.

Voici un exemple. Disons que j'ai deux processus écrivant tous les deux 8 octets à l'offset 1024. Le processus 1 écrit «11111111» et le processus 2 écrit «22222222». S'ils utilisent des E / S de fichier, alors vous pouvez imaginer, au fond de l'O / S, qu'il y a un tampon plein de 1 et un tampon plein de 2, tous deux dirigés au même endroit sur le disque. L'un d'eux y arrivera le premier, et l'autre une seconde. Dans ce cas, le second l'emporte. Cependant , si j'utilise l'approche des fichiers mappés en mémoire, le processus 1 va aller dans une mémoire de 4 octets, suivie d'une autre mémoire de 4 octets (supposons que ce n'est pas la taille maximale de la mémoire de stockage). Le processus 2 fera la même chose. En fonction du moment où les processus s'exécutent, vous pouvez vous attendre à voir l'un des éléments suivants:

11111111
22222222
11112222
22221111

La solution à cela est d'utiliser l'exclusion mutuelle explicite - ce qui est probablement une bonne idée de toute façon. Vous vous reposiez en quelque sorte sur l'O / S pour faire "la bonne chose" dans le cas d'E / S de fichier en lecture / écriture, de toute façon.

La primitive d'exclusion mutuelle de classification est le mutex. Pour les fichiers mappés en mémoire, je vous suggère de regarder un mutex mappé en mémoire, disponible en utilisant (par exemple) pthread_mutex_init ().

Modifier avec un seul piège: lorsque vous utilisez des fichiers mappés, il est tentant d'incorporer des pointeurs vers les données dans le fichier, dans le fichier lui-même (pensez à la liste liée stockée dans le fichier mappé). Vous ne voulez pas faire cela, car le fichier peut être mappé à différentes adresses absolues à des moments différents ou dans différents processus. Utilisez plutôt des décalages dans le fichier mappé.

tourbière
la source
1

La concurrence serait un problème. L'accès aléatoire est plus facile Les performances sont bonnes à excellentes. Facilité d'utilisation. Pas aussi bon. Portabilité - pas si chaud.

Je les ai utilisés sur un système solaire il y a longtemps, et ce sont mes pensées.

Paul Nathan
la source