Domanda

Sto cercando di ottimizzare la gestione di set di dati di grandi dimensioni utilizzando mmap. Un set di dati è compreso nell'intervallo di gigabyte. L'idea era di eseguire il mmap dell'intero file in memoria, consentendo a più processi di lavorare contemporaneamente sul set di dati (sola lettura). Non funziona come previsto però.

Come semplice test ho semplicemente mmap il file (usando il modulo Sys :: Mmap di perl, usando il " mmap " sub che credo sia mappato direttamente alla funzione C sottostante) e ho il processo dormire. Nel fare ciò, il codice impiega più di un minuto prima di tornare dalla chiamata mmap, nonostante questo test non faccia nulla - nemmeno una lettura - dal file mmap.

Suppongo che forse Linux richiedesse la lettura dell'intero file al primo mmap, quindi dopo che il file era stato mappato nel primo processo (mentre era inattivo), ho invocato un semplice test in un altro processo che ha provato per leggere i primi pochi megabyte del file.

Sorprendentemente, sembra che anche il secondo processo trascorra molto tempo prima di tornare dalla chiamata mmap, più o meno allo stesso tempo di mmap'ing il file la prima volta.

Mi sono assicurato che MAP_SHARED è in uso e che il processo che ha mappato il file la prima volta è ancora attivo (che non è stato terminato e che la mmap non è stata decompressa).

Mi aspettavo che un file mmapped mi permettesse di fornire a più processi di lavoro un accesso casuale efficace al file di grandi dimensioni, ma se ogni chiamata mmap richiede prima di leggere l'intero file, è un po 'più difficile. Non ho testato l'utilizzo di processi a lungo termine per vedere se l'accesso è veloce dopo il primo ritardo, ma mi aspettavo di usare MAP_SHARED e un altro processo separato sarebbe stato sufficiente.

La mia teoria era che mmap sarebbe tornato più o meno immediatamente e che Linux avrebbe caricato i blocchi più o meno su richiesta, ma il comportamento che sto vedendo è l'opposto, indicando che richiede la lettura dell'intero file ad ogni chiamata a mmap.

Hai idea di cosa sto facendo di sbagliato, o se ho completamente frainteso come dovrebbe funzionare mmap?

È stato utile?

Soluzione

Ok, ho trovato il problema. Come sospettato, né Linux né Perl erano da biasimare. Per aprire e accedere al file faccio qualcosa del genere:

#!/usr/bin/perl
# Create 1 GB file if you do not have one:
# dd if=/dev/urandom of=test.bin bs=1048576 count=1000
use strict; use warnings;
use Sys::Mmap;

open (my $fh, "<test.bin")
    || die "open: $!";

my $t = time;
print STDERR "mmapping.. ";
mmap (my $mh, 0, PROT_READ, MAP_SHARED, $fh)
    || die "mmap: $!";
my $str = unpack ("A1024", substr ($mh, 0, 1024));
print STDERR " ", time-$t, " seconds\nsleeping..";

sleep (60*60);

Se provi quel codice, non ci sono ritardi come quelli che ho trovato nel mio codice originale e dopo aver creato l'esempio minimo (fallo sempre, giusto!) il motivo è diventato improvvisamente ovvio.

L'errore era che nel mio codice ho trattato lo scalare $mh come una maniglia, qualcosa che è leggero e può essere spostato facilmente (leggi: passa per valore). Si scopre che in realtà è una stringa lunga GB, sicuramente non qualcosa che si desidera spostare senza creare un riferimento esplicito (lingua perl per un & Quot; pointer & Quot; / handle value). Pertanto, se è necessario archiviare in un hash o simile, assicurarsi di archiviare \$mh e cancellarlo quando è necessario utilizzarlo come ${$hash->{mh}}, in genere come primo parametro in un substrato o simile.

Altri suggerimenti

Se hai una versione relativamente recente di Perl, non dovresti usare Sys :: Mmap. Dovresti utilizzare il livello mmap di PerlIO.

Puoi pubblicare il codice che stai utilizzando?

Su sistemi a 32 bit lo spazio degli indirizzi per mmap() s è piuttosto limitato (e varia da SO a SO). Tieni presente che se stai utilizzando file multi-gigabyte e stai testando solo su un sistema a 64 bit. (Avrei preferito scriverlo in un commento ma non ho ancora abbastanza punti reputazione)

una cosa che può aiutare le prestazioni è l'uso di 'madvise (2)'. probabilmente più facilmente fatto tramite Inline :: C. 'madvise' ti consente di dire al kernel come sarà il tuo modello di accesso (ad esempio sequenziale, casuale, ecc.)

Sembra sorprendente. Perché non provare una versione C pura?

O prova il tuo codice su una diversa versione del sistema operativo / perl.

Vedi Wide Finder per perl prestazioni con mmap. Ma c'è un grosso trabocchetto. Se il tuo set di dati sarà su HD classico e leggerai da più processi, puoi facilmente accedere in modo casuale e il tuo IO scenderà a valori inaccettabili (20 ~ 40 volte).

Ok, ecco un altro aggiornamento. Usando Sys :: Mmap o PerlIO & Quot;: mmap & Quot; l'attributo funziona bene in perl, ma solo fino a 2 GB di file (il limite magico di 32 bit). Una volta che il file supera i 2 GB, vengono visualizzati i seguenti problemi:

Usando Sys :: Mmap e substr per accedere al file, sembra che substr accetta solo un int a 32 bit per il parametro position, anche su sistemi in cui perl supporta 64 bit. C'è almeno un bug pubblicato al riguardo:

# 62646: Lunghezza massima della stringa con substr

Usando open(my $fh, "<:mmap", "bigfile.bin"), una volta che il file è più grande di 2 GB, sembra che perl si bloccherà / o insisterà nel leggere l'intero file alla prima lettura (non so quale, non l'ho mai eseguito abbastanza a lungo per vedere se completato), con conseguente rallentamento delle prestazioni.

Non ho trovato alcuna soluzione alternativa a nessuno di questi, e attualmente sono bloccato con operazioni di file lenti (non mmap'ed) per lavorare su questi file. A meno che non trovo una soluzione alternativa, potrei dover implementare l'elaborazione in C o in un altro linguaggio di livello superiore che supporti meglio il mmap'ing di file enormi.

Se posso collegare il mio modulo: consiglierei di usare File :: Mappa anziché Sys :: Mmap . È molto più facile da usare ed è meno soggetto a crash rispetto a Sys :: Mmap.

È meglio che l'accesso a quel file sia ben casuale per giustificare un mmap completo. Se il tuo utilizzo non è distribuito uniformemente, probabilmente stai meglio con una ricerca, leggi in un'area appena sfornata ed elabora, libera, risciacqua e ripeti. E lavora con pezzi di multipli di 4k, diciamo 64k o giù di lì.

Una volta ho fatto un benchmark di molti algoritmi di corrispondenza del modello di stringa. Il mmaping dell'intero file è stato lento e inutile. Leggere su un buffer statico 32kish era meglio, ma non ancora particolarmente buono. Leggere su un pezzo appena malloced, elaborarlo e poi lasciarlo andare permette al kernel di fare miracoli sotto il cofano. La differenza di velocità era enorme , ma poi di nuovo la corrispondenza dei modelli è molto rapida in termini di complessità e si deve porre maggiore enfasi sull'efficienza di gestione di quanto forse sia normalmente necessario.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top