J'ai fait des recherches sur 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 les points suivants, par ordre d'importance:
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, elles peuvent entrer sans être copiées; si vous utilisez un appel système (par exemple Linux pread ()), 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, un défaut de page n'est probablement pas meilleur en termes de performances qu'un appel système, mais s'ils pas (c'est-à-dire déjà dans le cache du système d'exploitation), les performances devraient en théorie être bien meilleures.
À la baisse, il n'y a pas d'interface asynchrone avec 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.
J'ai utilisé un fichier mappé en mémoire pour implémenter une fonction de "remplissage automatique" pendant que l'utilisateur tape. J'ai bien 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 C
- style struct
, et nous effectuons une recherche binaire pour trouver les numéros de pièce correspondants en tant que types d'utilisateurs. Seules quelques pages mémoire du fichier sont réellement lues à partir du disque - quelles que soient les pages touchées lors de la recherche binaire.
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 chercher, d'écrire et de lire un fichier, vous le mappez en mémoire et accédez simplement aux bits où vous vous attendez.
Cela peut être très pratique et, selon l'interface de mémoire virtuelle, peut améliorer les performances. L'amélioration des performances peut se produire parce que le système d'exploitation gère désormais cet ancien "E/S de fichiers" ainsi que tous vos autres accès à la mémoire de programmation et peut (en théorie) tirer parti des 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. Cependant, cela dépend de la qualité de votre système de mémoire virtuelle sous-jacent. Anecdotes que j'ai entendu 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 aucune donnée empirique pour le confirmer. YMMV.
La concurrence entre en jeu lorsque vous envisagez la possibilité que plusieurs processus utilisent le même "fichier" via la mémoire mappée. Dans le modèle en lecture/écriture, si deux processus ont écrit dans la même zone du fichier, vous pouvez être à peu près assuré qu'une des données du processus arrivera dans le fichier, écrasant les données de l'autre processus. Vous obtiendriez l'un ou l'autre - mais pas un mélange étrange. Je dois admettre que je ne suis pas sûr que ce soit un comportement imposé par une norme, mais c'est quelque chose sur lequel vous pouvez à peu près 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 "mémoires de stockage", ce qui a pour conséquence que le système d'exploitation envoie les données sur le disque - éventuellement. Mais en attendant, on peut s'attendre à ce que des écritures se chevauchant.
Voici un exemple. Disons que j'ai deux processus qui écrivent 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 fichiers, 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 va y arriver en premier, et l'autre une seconde. Dans ce cas, le second gagne. Cependant, si j'utilise l'approche de fichier mappé en mémoire, le processus 1 va aller un magasin de mémoire de 4 octets, suivi par un autre magasin de mémoire de 4 octets (supposons que ce n'est pas la mémoire maximale taille du magasin). Le processus 2 fera la même chose. Selon le moment où les processus s'exécutent, vous pouvez vous attendre à voir l'un des éléments suivants:
11111111
22222222
11112222
22221111
La solution consiste à utiliser l'exclusion mutuelle explicite - ce qui est probablement une bonne idée en tout état de cause. Vous comptiez en quelque sorte sur l'O/S pour faire "la bonne chose" dans le cas d'E/S du fichier en lecture/écriture, de toute façon.
La primitive d'exclusion mutuelle de classement 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 problème: lorsque vous utilisez des fichiers mappés, il y a une tentation d'incorporer des pointeurs vers les données du 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 à différents moments ou dans différents processus. Utilisez plutôt des décalages dans le fichier mappé.
La concurrence serait un problème. L'accès aléatoire est plus facile Les performances vont de bonnes à excellentes. Facilité d'utilisation. Pas aussi bon. Portabilité - pas si chaud.
Je les ai utilisés sur un système Sun il y a longtemps, et ce sont mes pensées.