Quali sono i vantaggi dei file mappati in memoria?


89

Ho ricercato file mappati in memoria per un progetto e apprezzerei qualsiasi pensiero da parte di persone che li hanno già utilizzati o hanno deciso di non utilizzarli, e perché?

In particolare, mi preoccupa quanto segue, in ordine di importanza:

  • concorrenza
  • accesso casuale
  • prestazione
  • facilità d'uso
  • portabilità

Risposte:


56

Penso che il vantaggio sia davvero che riduci la quantità di dati da copiare richiesta rispetto ai metodi tradizionali di lettura di un file.

Se l'applicazione può utilizzare i dati "sul posto" in un file mappato in memoria, può entrare senza essere copiato; se si utilizza una chiamata di sistema (ad es. pread () di Linux), questo in genere implica che il kernel copi i dati dai propri buffer nello spazio utente. Questa copia extra non solo richiede tempo, ma riduce l'efficacia delle cache della CPU accedendo a questa copia extra dei dati.

Se i dati devono essere effettivamente letti dal disco (come nell'I / O fisico), il sistema operativo deve ancora leggerli, un errore di pagina probabilmente non è migliore in termini di prestazioni di una chiamata di sistema, ma se non farlo (cioè già nella cache del sistema operativo), le prestazioni dovrebbero in teoria essere molto migliori.

Sul lato negativo, non esiste un'interfaccia asincrona per i file mappati in memoria: se si tenta di accedere a una pagina che non è mappata, viene generato un errore di pagina, quindi il thread attende l'I / O.


L'ovvio svantaggio dei file mappati in memoria è su un sistema operativo a 32 bit: è possibile esaurire facilmente lo spazio degli indirizzi.


4
Almeno su Windows è possibile mappare più visualizzazioni a 32 bit di un file mmap più grande, il che può essere più efficiente rispetto al tentativo di gestire file molto grandi utilizzando la normale funzione CRT
Martin Beckett

@MarkR You ha scritto "la sua copia extra non solo richiede tempo, ma riduce l'efficacia delle cache della CPU accedendo a questa copia extra dei dati. ". ( enfasi mia). Puoi per favore spiegare come la copia aggiuntiva del buffer nel kernel ostacola l'efficacia delle cache della CPU?
Geek

4
@ Geek accede al doppio della memoria = il doppio della cache sprecata (molto approssimativamente).
user253751

49

Ho utilizzato un file mappato in memoria per implementare una funzione di "completamento automatico" durante la digitazione. Ho oltre 1 milione di codici prodotto memorizzati in un unico file di indice. Il file ha alcune tipiche informazioni di intestazione, ma la maggior parte del file è una serie enorme di record di dimensioni fisse ordinati nel campo chiave.

In fase di esecuzione, il file viene mappato in memoria, sottoposto a cast su un array in Cstile structe viene eseguita una ricerca binaria per trovare i numeri di parte corrispondenti durante la digitazione dell'utente. Solo poche pagine di memoria del file vengono effettivamente lette dal disco, a seconda di quale pagina viene raggiunta durante la ricerca binaria.

  • Concorrenza: ho avuto un problema di implementazione in cui a volte la memoria mappava il file più volte nello stesso spazio di elaborazione. Questo era un problema che ricordo perché a volte il sistema non riusciva a trovare un blocco di memoria virtuale abbastanza grande per mappare il file. La soluzione era mappare il file solo una volta e thunk tutte le chiamate a esso. In retrospettiva, l'utilizzo di un servizio Windows completo sarebbe stato interessante.
  • Accesso casuale - La ricerca binaria è sicuramente ad accesso casuale e velocissima
  • Prestazioni: la ricerca è estremamente veloce. Man mano che gli utenti digitano, una finestra popup visualizza un elenco di numeri di parte del prodotto corrispondenti, l'elenco si restringe man mano che continuano a digitare. Non vi è alcun ritardo evidente durante la digitazione.

1
La ricerca binaria non sarebbe lenta man mano che le pagine vengono lette ad ogni tentativo? O il sistema operativo è abbastanza intelligente da affrontare questo problema in modo efficiente?
jjxtra

1
Suppongo che l'utilizzo di I / O mappato in memoria sia un po 'dispendioso per la ricerca binaria, poiché la ricerca accederà solo a poche chiavi singole in posizioni di memoria relativamente distanti, ma il sistema operativo caricherà in 4k pagine per ciascuna di tali richieste. Ma poi di nuovo, il file con le parti non cambia molto, quindi la cache aiuta a coprirlo. Ma a rigor di termini, credo che la ricerca / lettura tradizionale sarebbe migliore qui. Infine, 1 mil non è molto di questi tempi. Perché non tenere tutto nella RAM?
il suino

5
@ the swine e PsychoDad la mia risposta originale era del 2008 e l'effettiva implementazione di questa funzione di completamento automatico mappato in memoria era intorno al 2004-2005 circa. Il consumo di 800-1000 MB di memoria fisica per caricare l'intero file non era una buona soluzione per la nostra base di utenti. La soluzione mappata in memoria è stata molto veloce ed efficiente. Ha preso a calci in culo e lo ricordo con affetto sin dai miei primi giorni di sviluppatore junior. :)
Brian Ensink

@BrianEnsink: ok, ha senso. non mi aspettavo che ogni voce fosse fino a 1kB. poi ovviamente l'approccio a pagine diventa più efficiente. bello :)
il suino

22

I file mappati in memoria possono essere utilizzati per sostituire l'accesso in lettura / scrittura o per supportare la condivisione simultanea. Quando li usi per un meccanismo, ottieni anche l'altro.

Piuttosto che cercare, scrivere e leggere in giro in un file, lo mappi nella memoria e accedi semplicemente ai bit dove ti aspetti che siano.

Questo può essere molto utile e, a seconda dell'interfaccia della memoria virtuale, può migliorare le prestazioni. Il miglioramento delle prestazioni può verificarsi perché il sistema operativo ora riesce a gestire questo precedente "file I / O" insieme a tutti gli altri accessi alla memoria programmatica e può (in teoria) sfruttare gli algoritmi di paging e così via che sta già utilizzando per supportare memoria virtuale per il resto del programma. Tuttavia, dipende dalla qualità del sistema di memoria virtuale sottostante. Aneddoti che ho sentito dire che i sistemi di memoria virtuale Solaris e * BSD possono mostrare miglioramenti delle prestazioni migliori rispetto al sistema VM di Linux, ma non ho dati empirici per supportarlo. YMMV.

La concorrenza entra in gioco quando si considera la possibilità che più processi utilizzino lo stesso "file" attraverso la memoria mappata. Nel modello di lettura / scrittura, se due processi scrivevano nella stessa area del file, si poteva essere praticamente certi che uno dei dati del processo sarebbe arrivato nel file, sovrascrivendo i dati dell'altro processo. Otterresti uno o l'altro, ma non qualche strana mescolanza. Devo ammettere che non sono sicuro che questo sia un comportamento imposto da qualsiasi standard, ma è qualcosa su cui potresti fare affidamento. (In realtà è una buona domanda di follow-up!)

Nel mondo mappato, al contrario, immagina due processi entrambi di "scrittura". Lo fanno facendo "archivi di memoria", che si traducono in O / S che impagina i dati su disco - alla fine. Ma nel frattempo ci si può aspettare che si verifichino scritture sovrapposte.

Ecco un esempio. Supponiamo che io abbia due processi che scrivono entrambi 8 byte all'offset 1024. Il processo 1 scrive "11111111" e il processo 2 scrive "22222222". Se usano l'I / O di file, allora puoi immaginare, in fondo all'O / S, ci sia un buffer pieno di 1 e un buffer pieno di 2, entrambi diretti nella stessa posizione sul disco. Uno di loro arriverà per primo e l'altro il secondo. In questo caso, vince il secondo. Tuttavia , se utilizzo l'approccio ai file mappati in memoria, il processo 1 andrà in un archivio di memoria di 4 byte, seguito da un altro archivio di memoria di 4 byte (supponiamo che non sia la dimensione massima dell'archivio di memoria). Il processo 2 farà la stessa cosa. In base a quando vengono eseguiti i processi, puoi aspettarti di vedere uno dei seguenti:

11111111
22222222
11112222
22221111

La soluzione a questo problema è usare l'esclusione reciproca esplicita, che è probabilmente una buona idea in ogni caso. In ogni caso, ti stavi facendo affidamento sull'O / S per fare "la cosa giusta" nel caso di I / O di file di lettura / scrittura.

La primitiva di classificazione a mutua esclusione è il mutex. Per i file mappati in memoria, suggerirei di guardare un mutex mappato in memoria, disponibile utilizzando (ad esempio) pthread_mutex_init ().

Modifica con un solo trucco: quando si utilizzano file mappati, c'è la tentazione di incorporare puntatori ai dati nel file, nel file stesso (si pensi all'elenco collegato memorizzato nel file mappato). Non si desidera farlo, poiché il file potrebbe essere mappato a indirizzi assoluti diversi in momenti diversi o in processi diversi. Utilizzare invece gli offset all'interno del file mappato.


1

La concorrenza sarebbe un problema. L'accesso casuale è più semplice Le prestazioni vanno da buone a ottime. Facilità di utilizzo. Non buono quanto. Portabilità - non così caldo.

Li ho usati su un sistema solare molto tempo fa, e questi sono i miei pensieri.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.