Domanda

Ho scritto un convertitore che prende i file XML di OpenStreetMap e li converte in un formato binario runtime di rendering che è in genere circa il 10% della dimensione originale. le dimensioni dei file di ingresso sono in genere 3GB e più grandi. I file di input non vengono caricati in memoria tutto in una volta, ma in streaming come punti e poligoni sono raccolti, poi un BSP viene eseguito su di loro e il file viene emesso. Recentemente su file di dimensioni maggiori si esaurisce la memoria e muore (quello in questione ha 14 milioni di punti e 1 milione di poligoni). In genere il mio programma sta usando circa 1 GB a 1,2 GB di RAM quando questo accade. Ho provato aumentare la memoria virtuale da 2 a 8GB (su XP), ma questo cambiamento ha fatto nessun effetto. Inoltre, dal momento che questo codice è open-source mi piacerebbe farlo funzionare indipendentemente dalla RAM disponibile (anche se più lenta), che gira su Windows, Linux e Mac.

Quali tecniche che posso usare per evitare di dover correre la memoria? Elaborare i dati in piccoli sottoinsiemi e quindi unendo i risultati finali? Usando il mio tipo di memoria virtuale di gestore? Tutte le altre idee?

È stato utile?

Soluzione

In primo luogo, su un sistema a 32 bit, sarà sempre limitato a 4 GB di memoria, non importa le impostazioni di file di paging. (E di questi, solo 2 GB sarà disponibile per il processo su Windows. Su Linux, si hanno in genere intorno a 3GB disponibili)

Quindi, la prima soluzione più ovvia è quella di passare ad un sistema operativo a 64-bit, e compilare l'applicazione a 64 bit. Che ti dà un enorme spazio di memoria virtuale da usare, e il sistema operativo sarà scambiare i dati dentro e fuori il file di paging, se necessario, per mantenere le cose a lavorare.

In secondo luogo, l'allocazione blocchi più piccoli di memoria alla volta può aiutare. Spesso è più facile trovare 4 pezzi da 256 MB di memoria libera di un pezzo da 1 GB.

In terzo luogo, dividere il problema. Non elaborare l'intero set di dati in una sola volta, ma cercare di caricare e trattare solo una piccola sezione alla volta.

Altri suggerimenti

Hai controllato per garantire che non vi siano perdite di memoria da qualche parte?

Dal momento che il programma è portabile su Linux, vi suggerisco di correre sotto Valgrind per assicurarsi.

Sembra che si sta già facendo una SAX approccio al trattamento XML ( caricamento del XML, come si va, invece di tutti in una volta).

La soluzione è quasi sempre quello di modificare l'algoritmo in modo che taglia il problema in parti più piccole. Fisicamente non allocare la quantità di memoria in una sola volta, di leggere solo quello che ti serve, processo, poi scrivere fuori.

A volte è possibile espandere la memoria tramite utilizzando il disco rigido, invece, quando necessario nel vostro algoritmo.

Se non è possibile scindere l'algoritmo, probabilmente vuole qualcosa come file mappati in memoria .

Nel peggiore dei casi si può provare a usare qualcosa come VirtualAlloc se siete su un sistema Windows. Se siete su un sistema a 32 bit si può provare a usare qualcosa come Physical Address Extension (PAE) .

Si potrebbe anche valutare l'ipotesi di limitazioni di input per il programma, e avere una diversa per i sistemi a 32-bit e 64-bit.

Ho il sospetto che i vostri problemi di memoria sono di mantenere l'albero BSP in memoria. In modo da mantenere il BSP sul disco e mantenere solo alcuni frammenti nella memoria. Questo dovrebbe essere abbastanza facile con BSP, come la struttura si presta più di alcune altre strutture ad albero, e la logica dovrebbe essere semplice. Per essere efficiente e di memoria che si potrebbe avere una cache w / bandierina sporca, con la dimensione della cache è impostato su memoria disponibile un po 'meno per il respiro.

Supponendo che si utilizza Windows XP, se si è solo appena oltre il limite di memoria e non desiderio o hanno il tempo di rielaborare il codice come suggerito sopra, è possibile aggiungere l'opzione / 3GB al boot.ini file di nofollow noreferrer e poi è solo una questione di impostazione di un interruttore linker per ottenere un 1 GB in più della memoria.

Dovete capire che la memoria virtuale è diverso da "RAM" in quanto la quantità di memoria virtuale che si sta utilizzando è l'importo totale che hai prenotato, mentre la memoria reale (in Windows la sua chiamata working set) è la memoria che hai effettivamente modificato o bloccato.

Come qualcun altro ha sottolineato, su piattaforme Windows a 32 bit il limite di memoria virtuale è 2 gigabyte se non si imposta la bandiera speciale per 3 gigabyte e può garantire che tutti i puntatori sia nel codice e le librerie si utilizzano solo per uso puntatori senza segno.

Quindi, o costringere gli utenti a 64 bit o il monitoraggio della memoria virtuale e tappatura vostra dimensione del blocco massimo a qualcosa che si adatta comodamente all'interno dei limiti imposti dai sistemi operativi a 32 bit sarebbe il mio consiglio.

Ho sbattuto contro il muro a 32 bit in Windows, ma non hanno alcuna esperienza con il lavoro intorno a queste limitazioni in Linux in modo ho parlato solo il lato di Windows delle cose.

32 bit XP lo spazio massimo indirizzo del programma è di 2GB. Poi ci sono la frammentazione a causa di DLL ei driver di carico fino al tuo spazio di indirizzi. Infine, avete il problema del vostro frammentazione heap.

La vostra mossa migliore è solo quello di farla finita e correre come un processo a 64 bit (su un sistema a 64-bit). Improvvisamente tutti questi problemi vanno via. È possibile utilizzare un mucchio meglio per mitigare gli effetti mucchio di frammentazione, e si può provare a utilizzare VirtualAlloc di afferrare la tua memoria in un unico grande pezzo contigui (e poi si arriva a gestire da lì!) Per scoraggiare di DLL / driver da frammentare esso.

Infine, è possibile dividere il BSP attraverso i processi. Complicato e doloroso, e francamente solo mettendo su disco sarebbe stato più facile, ma in teoria si potrebbe ottenere prestazioni migliori da avere un gruppo di processi che scambiano informazioni, se si può tenere tutto residenti (e supponendo che si può essere più intelligente di memoria rispetto al sistema operativo in grado di gestire file di buffer ... che è un grosso se). Ogni processo avrebbe bisogno di molto meno memoria e quindi non dovrebbe correre per il limite di spazio indirizzo di 2 GB. Naturalmente, si bruciano attraverso RAM / scambiare molto più veloce.

È possibile mitigare gli effetti della frammentazione dello spazio degli indirizzi assegnando blocchi più piccoli. Questo avrà altri effetti collaterali sgradevoli, ma si potrebbe seguire una politica di backoff in cui si afferra blocchi più piccoli e più piccoli di memoria se non si riesce a allocare con successo. Spesso questo approccio semplice ti porterà un programma che funziona quando altrimenti non sarebbe, ma il resto del tempo si esibisce così come potrebbe.

Il ragazzo, non a 64 bit solo il suono in modo molto più bello rispetto alle altre scelte?

Come ti allocazione di memoria per i punti? Stai assegnano punto uno alla volta (per esempio pt = new Point). Poi a seconda delle dimensioni del punto, un po 'di memoria può avere sprecato. Per esempio sulla memoria finestre viene allocata in multipli di 16 byte, quindi, anche se si chiede tenta di allocare 1 byte, OS sarà effettivamente allocare 16 byte.

Se questo è il caso, utilizzando un allocatore di memoria può aiutare. Si può fare un rapido controllo utilizzando STL allocatore. (Oltre caricare il nuovo operatore per la classe Point e utilizzare l'allocatore STL per allocare la memoria piuttosto che 'malloc' o di default nuovo operatore).

Potrebbe non essere allocare e deallocare memoria in modo ottimale. Come altri hanno fatto notare, si può essere che perde la memoria e non saperlo. Debugging e allocazione di memoria ottimizzazione richiederanno tempo.

Se non si vuole spendere tempo a ottimizzare l'utilizzo della memoria, perché non provare la conservatore Garbage Collector ? Si tratta di una sostituzione plug-in per malloc () / nuovo e free (). In realtà, free () è un no-op, così puoi semplicemente rimuovere quelle chiamate dal proprio programma. Se, invece, è a mano di ottimizzare il vostro programma e gestire un pool di memoria, come suggerito in precedenza, si finisce per fare un sacco di lavoro che il CGC già fa per voi.

È necessario trasmettere la tua uscita così come il vostro input. Se il formato di uscita non è orientato al flusso, in considerazione di fare secondo passaggio. Ad esempio, se il file di output inizia con somma di controllo / dimensione dei dati, lascia spazio al primo passaggio e cercare / scrittura a quello spazio in seguito.

E 'il suono come si sta facendo TXT per la conversazione in modo binario perché avete bisogno di avere tutti i dati nella memoria ?.
Non puoi semplicemente leggere un primitivo da txt (xml) quindi salvare per BinaryStream?

Se si vuole essere memoria di dimensioni indipendenti, è necessario un algoritmo di dimensione indipendente. Non importa quanto sia grande il vostro RAM è, se non si dispone di utilizzo della memoria sotto controllo, si sta andando a sbattere contro il bordo.

Date un'occhiata al minimo pezzo di informazioni si può eventualmente utilizzare per produrre un po 'di uscita. Poi pensare ad un modo per dividere l'ingresso in blocchi di queste dimensioni.

Ora che sembra facile, non è vero? (Contento non ho farlo :))

Non è necessario passare a macchine a 64 bit, non è necessario la maggior parte delle 1000 cose suggeriti da altri. Quello che vi serve è un algoritmo più riflessivo.

Qui ci sono alcune cose che puoi fare per dare una mano a questa situazione:

  • Se siete su Windows, utilizzare file Maps ( codice di esempio ). Questo darà l'accesso al file tramite un unico puntatore del buffer come se si legge l'intero file in memoria, solo senza in realtà farlo. Le versioni più recenti di Linux Kernel hanno un meccanismo simile.
  • Se è possibile, e sembra che si potrebbe, la scansione del file in sequenza ed evitare di creare un DOM in memoria. Questo diminuisce notevolmente il carico-tempo così come i requisiti di memoria.
  • Usa pool di memoria! Si dovrà probabilmente molti piccoli oggetti, come i nodi, i punti e quant'altro. Utilizzare una memoria pool per aiutare fuori (sto supponendo che si sta utilizzando un linguaggio non gestito. Ricerca di assegnazione pool e pool di memoria).
  • Se si utilizza un linguaggio gestito, almeno spostare questa particolare parte in un linguaggio non gestito e prendere il controllo della lettura della memoria e file. lingue gestite hanno un overhead non banale sia in occupazione di memoria e prestazioni. (Sì, so che questo è etichettato "C ++" ...)
  • Tentativo di progettare un algoritmo sul posto, dove si legge e trattare solo la quantità minima di dati alla volta, in modo che le richieste di memoria sarebbe andato giù.

Infine, vorrei sottolineare che operazioni complesse richiedono misure complesse. Se si pensa che si può permettere una macchina a 64-bit con 8 GB di RAM, poi basta usare "leggere il file in memoria, i dati di processo, scrivere l'output" algoritmo, anche se ci vuole un giorno per finire.

c'è una buona tecnica per questo, è quello di memorizzare alcuni casi in file, e dopo di loro ottenere quando si ha bisogno di usarli.

questa tecnica è utilizzata da molti software open source come Doxygen per essere scalabile quando è necessaria una grande quantità di memoria.

Questa è una vecchia questione, ma, dal momento che ho fatto di recente la stessa cosa ....

Non c'è una risposta semplice. In un mondo ideale che ci si utilizza una macchina con un enorme spazio di indirizzo (ad esempio a 64 bit), e grandi quantità di memoria fisica. lo spazio di indirizzi enorme da sola non è sufficiente o sarà solo thrash. In questo caso analizzare il file XML in un database, e con le query appropriate, tirare fuori quello che ti serve. Molto probabilmente questo è ciò che si fa OSM (Credo che il mondo è di circa 330GB).

In realtà sto ancora usando XP a 32 bit per ragioni di opportunità.

E 'un compromesso tra lo spazio e la velocità. Si può fare praticamente qualsiasi cosa in qualsiasi quantità di memoria che fornisce non ti importa quanto tempo ci vuole. Utilizzando strutture STL è possibile analizzare tutto quello che vuoi, ma presto a corto di memoria. È possibile definire il proprio ripartitori che scambieranno, ma ancora una volta, sarà inefficiente perché le mappe, vettori, ecc set non sanno quello che stai facendo.

L'unico modo che ho trovato per far funzionare il tutto in una piccola impronta su una macchina a 32 bit è stato quello di riflettere molto attentamente su quello che stavo facendo e ciò che era necessario quando e rompere il compito in pezzi. Efficiente della memoria (mai utilizza più di ~ 100MB), ma non in maniera massiccia veloce, ma poi non importa -? Quante volte si devono analizzare i dati XML

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