Modo professionale per produrre un grosso problema senza riempire enormi matrici: C ++, memoria libera da parte di una matrice

20

Sto sviluppando una simulazione fisica e, dato che sono piuttosto nuovo alla programmazione, continuo a riscontrare problemi nella produzione di programmi di grandi dimensioni (principalmente problemi di memoria). Conosco l'allocazione e l'eliminazione della memoria dinamica (nuovo / cancella, ecc.), Ma ho bisogno di un approccio migliore per strutturare il programma.

Diciamo che sto simulando un esperimento in esecuzione per alcuni giorni, con una frequenza di campionamento molto ampia. Dovrei simulare un miliardo di campioni e analizzarli.

Come versione super semplificata, diremo che un programma prende tensioni V [i] e le somma in cinque:

vale a dire. NewV [0] = V [0] + V [1] + V [2] + V [3] + V [4]

then NewV [1] = V [1] + V [2] + V [3] + V [4] + V [5]

then NewV [2] = V [2] + V [3] + V [4] + V [5] + V [6] ... e questo va avanti per un miliardo di campioni.

Alla fine, avrei V [0], V [1], ..., V [1000000000], quando invece gli unici che avrei bisogno di memorizzare per il prossimo passo sono gli ultimi 5 V [è.

Come faccio a eliminare / deallocare parte dell'array in modo che la memoria sia di nuovo libera da usare (diciamo V [0] dopo la prima parte dell'esempio dove non è più necessario)? Esistono alternative a come strutturare un programma di questo tipo?

Ho sentito parlare di malloc / free, ma ho sentito che non dovrebbero essere usati in C ++ e che ci sono alternative migliori.

Grazie mille!

TLDR; cosa devo fare con le parti di array (elementi individuali) Non ho più bisogno di occuparmi di un'enorme quantità di memoria?

    
posta Drummermean 24.01.2017 - 14:28
fonte

3 risposte

58

Quello che descrivi, "smoothing by fives", è un filtro digitale a risposta impulsiva finita (FIR). Tali filtri sono implementati con buffer circolari. Si mantengono solo gli ultimi valori N, si mantiene un indice nel buffer che indica dove si trova il valore più vecchio, si sovrascrive il valore corrente più vecchio con uno più recente ad ogni passaggio e si esegue l'indice, in modo circolare, ogni volta. / p>

Conservi i tuoi dati raccolti, che stai per ridurre, su disco.

A seconda del tuo ambiente, questo potrebbe essere uno di quei posti in cui è meglio ricevere un aiuto esperto. In un'università, metti una nota nella bacheca del Dipartimento di Informatica, offrendo gli stipendi degli studenti (o anche i tassi di consulenza degli studenti) per alcune ore di lavoro, per aiutarti a scricchiolare i tuoi dati. O forse offri punti di opportunità di ricerca universitari. O qualcosa del genere.

    
risposta data 24.01.2017 - 15:28
fonte
13

Ogni problema può essere risolto aggiungendo un ulteriore livello di riferimento indiretto. Quindi fallo.

Non puoi cancellare parte di un array in C ++. Ma puoi creare un nuovo array contenente solo i dati che desideri conservare, quindi eliminare quello vecchio. Quindi puoi costruire una struttura dati che ti permetta di "rimuovere" elementi che non vuoi dal fronte. Quello che farà in realtà è creare un nuovo array e copiare gli elementi non rimossi in quello nuovo, quindi eliminare il vecchio.

Oppure potresti usare std::deque , che può già farlo in modo efficace. deque , o "coda a doppio attacco", è una struttura di dati destinata ai casi in cui stai eliminando elementi da un capo mentre aggiungi elementi all'altro.

    
risposta data 24.01.2017 - 15:13
fonte
4

Le risposte FIR e SMA che hai ottenuto sono valide nel tuo caso, tuttavia vorrei cogliere l'occasione per portare avanti un approccio più generico.

Quello che hai qui è uno flusso di dati: invece di strutturare il tuo programma in 3 grandi passi (ottenere dati, calcolare, risultato di output) che richiedono il caricamento di tutti i dati in memoria contemporaneamente, puoi invece lo struttura come pipeline .

Una pipeline inizia con uno stream, lo trasforma e lo spinge verso un sink.

Nel tuo caso, la pipeline ha il seguente aspetto:

  1. Leggi gli elementi dal disco, emetti gli elementi uno alla volta
  2. Ricevi gli articoli uno alla volta, per ogni oggetto ricevuto emettono gli ultimi 5 ricevuti (dove entra il buffer circolare)
  3. Ricevi gli elementi 5 alla volta, per ogni gruppo calcolare il risultato
  4. Ricevi il risultato, scrivilo su disco

C ++ tende ad usare iteratori piuttosto che flussi, ma per essere onesti i flussi sono più facili da modellare (esiste una proposta per intervalli che sarebbe simile agli stream):

template <typename T>
class Stream {
public:
    virtual boost::optional<T> next() = 0;
    virtual ~Stream() {}
};

class ReaderStream: public Stream<Item> {
public:
    boost::optional<Item> next() override final;

private:
    std::ifstream file;
};

class WindowStream: public Stream<Window> {
public:
    boost::optional<Window> next() override final;

private:
    Window window;
    Stream<Item>& items;
};

class ResultStream: public Stream<Result> {
public:
    boost::optional<Result> next() override final;

private:
    Stream<Window>& windows;
};

E poi, la pipeline assomiglia a:

ReaderStream itemStream("input.txt");
WindowStream windowStream(itemsStream, 5);
ResultStream resultStream(windowStream);
std::ofstream results("output.txt", std::ios::binary);

while (boost::optional<Result> result = resultStream.next()) {
    results << *result << "\n";
}

Gli stream non sono sempre applicabili (non funzionano quando è necessario un accesso casuale ai dati), ma quando lo sono, vanno a braccetto: operando su una piccola quantità di memoria si mantiene tutto nella cache della CPU.

Un'altra nota: sembra che il tuo problema potrebbe essere "imbarazzantemente parallelo", potresti voler dividere il tuo grosso file in blocchi (tieni presente, per l'elaborazione di Windows 5, che devi avere 4 elementi comuni in ogni confine) e quindi elaborare i blocchi in parallelo.

Se la CPU è il collo di bottiglia (e non I / O), puoi accelerarlo avviando un processo per core che hai dopo aver diviso i file in quantità approssimativamente uguali.

    
risposta data 25.01.2017 - 11:10
fonte

Leggi altre domande sui tag