it-swarm.com.de

Professioneller Weg, um ein großes Problem zu erzeugen, ohne große Arrays zu füllen: C ++, freier Speicher von einem Teil eines Arrays

Ich entwickle eine Physiksimulation und da ich ziemlich neu in der Programmierung bin, stoße ich immer wieder auf Probleme bei der Erstellung großer Programme (hauptsächlich Speicherprobleme). Ich kenne die dynamische Speicherzuweisung und das Löschen (neu/löschen usw.), aber ich brauche einen besseren Ansatz für die Strukturierung des Programms.

Angenommen, ich simuliere ein Experiment, das einige Tage läuft, mit einer sehr großen Abtastrate. Ich müsste eine Milliarde Proben simulieren und sie überfahren.

Als super vereinfachte Version nehmen wir an, ein Programm nimmt die Spannungen V [i] und summiert sie in fünf:

d.h. NewV [0] = V [0] + V [1] + V [2] + V [3] + V [4]

dann ist NewV [1] = V [1] + V [2] + V [3] + V [4] + V [5]

dann ist NewV [2] = V [2] + V [3] + V [4] + V [5] + V [6] ... und das geht für eine Milliarde Proben weiter.

Am Ende hätte ich V [0], V [1], ..., V [1000000000], wenn stattdessen nur die letzten 5 V [i] für den nächsten Schritt gespeichert werden müssten. s.

Wie würde ich Teil des Arrays löschen/freigeben, damit der Speicher wieder frei verwendet werden kann (sagen wir V [0])? nach dem ersten Teil des Beispiels, wo es nicht mehr benötigt wird)? Gibt es Alternativen zur Strukturierung eines solchen Programms?

Ich habe von malloc/free gehört, aber gehört, dass sie nicht in C++ verwendet werden sollten und dass es bessere Alternativen gibt.

Vielen Dank!

tldr; Was tun mit Teilen von Arrays (einzelnen Elementen), die ich nicht mehr benötige und die viel Speicherplatz beanspruchen?

20
Drummermean

Was Sie beschreiben, "Glätten um fünf", ist ein digitaler Filter mit endlicher Impulsantwort (FIR). Solche Filter werden mit kreisförmigen Puffern implementiert. Sie behalten nur die letzten N Werte bei, Sie behalten einen Index im Puffer, der Ihnen sagt, wo sich der älteste Wert befindet, Sie überschreiben den aktuell ältesten Wert bei jedem Schritt mit dem neuesten und Sie verschieben den Index jedes Mal zirkulär.

Sie speichern Ihre gesammelten Daten, die Sie verarbeiten möchten, auf der Festplatte.

Abhängig von Ihrer Umgebung ist dies möglicherweise einer der Orte, an denen Sie besser erfahrene Hilfe erhalten. An einer Universität stellen Sie eine Notiz an die Pinnwand des Fachbereichs Informatik, in der Sie die Löhne der Studenten (oder sogar die Beratungsraten für Studenten) für ein paar Stunden Arbeit anbieten, um Ihre Daten zu verarbeiten. Oder Sie bieten Undergraduate Research Opportunity-Punkte an. Oder so.

58
John R. Strohm

Jedes Problem kann durch Hinzufügen einer zusätzlichen Indirektionsebene gelöst werden. Also mach das.

Sie können einen Teil eines Arrays in C++ nicht löschen. Sie können jedoch ein neues Array erstellen, das nur die Daten enthält, die Sie behalten möchten, und dann das alte löschen. So können Sie eine Datenstruktur erstellen, mit der Sie Elemente, die Sie nicht möchten, von vorne "entfernen" können. Tatsächlich wird ein neues Array erstellt, die nicht entfernten Elemente in das neue kopiert und dann das alte gelöscht.

Oder Sie könnten einfach std::deque, was dies bereits effektiv tun kann. deque oder "Double-Ended Queue" ist eine Datenstruktur, die für Fälle vorgesehen ist, in denen Sie Elemente von einem Ende löschen, während Sie dem anderen Elemente hinzufügen.

13
Nicol Bolas

Die FIR- und SMA-Antworten, die Sie erhalten haben, sind in Ihrem Fall gut, ich möchte jedoch die Gelegenheit nutzen, um einen allgemeineren Ansatz voranzutreiben.

Was Sie hier haben, ist ein Stream von Daten: Anstatt Ihr Programm in 3 großen Schritten (Daten abrufen, berechnen, Ergebnis ausgeben) zu strukturieren, bei denen alle Daten gleichzeitig in den Speicher geladen werden müssen, können Sie es stattdessen strukturieren als Pipeline.

Eine Pipeline beginnt mit einem Stream, transformiert ihn und schiebt ihn zu einer Senke.

In Ihrem Fall sieht die Pipeline folgendermaßen aus:

  1. Lesen Sie Elemente von der Festplatte und senden Sie sie einzeln aus
  2. Empfangen Sie nacheinander Artikel. Geben Sie für jeden empfangenen Artikel die letzten 5 empfangenen aus (wo Ihr Ringpuffer eingeht).
  3. Erhalten Sie jeweils 5 Elemente, und berechnen Sie für jede Gruppe das Ergebnis
  4. Erhalten Sie das Ergebnis und schreiben Sie es auf die Festplatte

C++ verwendet eher Iteratoren als Streams, aber um ehrlich zu sein, sind Streams einfacher zu modellieren (es gibt einen Vorschlag für Bereiche, der Streams ähnlich wäre):

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;
};

Und dann sieht die Pipeline so aus:

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";
}

Streams sind nicht immer anwendbar (sie funktionieren nicht, wenn Sie zufälligen Zugriff auf die Daten benötigen), aber wenn sie funktionieren, rocken sie: Wenn Sie mit einer sehr kleinen Speichermenge arbeiten, behalten Sie alles im CPU-Cache.


Ein weiterer Hinweis: Es scheint, als ob Ihr Problem "peinlich parallel" ist. Möglicherweise möchten Sie Ihre große Datei in Blöcke aufteilen (denken Sie bei der Verarbeitung in Fenstern mit 5 Fenstern daran, dass Sie an jeder Grenze 4 gemeinsame Elemente haben müssen). und dann die Chunks parallel verarbeiten.

Wenn die CPU der Engpass ist (und nicht die E/A), können Sie dies beschleunigen, indem Sie einen Prozess pro Kern starten, den Sie haben, nachdem Sie die Dateien in ungefähr gleiche Mengen aufgeteilt haben.

4
Matthieu M.