web-dev-qa-db-ja.com

巨大な配列を埋めることなく大きな問題を生成する専門的な方法:C ++、配列の一部からメモリを解放

私は物理シミュレーションを開発していますが、プログラミングに慣れていないため、大きなプログラム(主にメモリの問題)を作成するときに問題が発生し続けます。動的なメモリの割り当てと削除(新規/削除など)については知っていますが、プログラムを構造化するためのより良いアプローチが必要です。

非常に大きなサンプリングレートで数日間実行されている実験をシミュレーションしているとしましょう。 10億のサンプルをシミュレートし、それらを実行する必要があります。

非常に簡略化したバージョンとして、プログラムは電圧V [i]を取り、それらを5で合計するとします。

つまり、NewV [0] = V [0] + V [1] + V [2] + V [3] + V [4]

次に、NewV [1] = V [1] + V [2] + V [3] + V [4] + V [5]

次に、NewV [2] = V [2] + V [3] + V [4] + V [5] + V [6] ...そして、これは10億のサンプルで続きます。

最後に、V [0]、V [1]、...、V [1000000000]があります。代わりに、次のステップで保存する必要があるのは最後の5つのV [i]だけです。 s。

配列のpartを削除/割り当て解除して、メモリを再び自由に使用できるようにするにはどうすればよいですか(V [0]など)不要になった例の最初の部分の後)?そのようなプログラムを構成する方法の代替はありますか?

Malloc/freeについて聞いたことがありますが、それらはC++では使用すべきではなく、より良い代替案があると聞いています。

どうもありがとう!

tldr;配列の一部(個々の要素)をどうするか大量のメモリを消費している必要がなくなった

20
Drummermean

「5による平滑化」とは、有限インパルス応答(FIR)デジタルフィルターのことです。このようなフィルターは循環バッファーで実装されます。最後のN個の値のみを保持し、最も古い値がどこにあるかを示すインデックスをバッファに保持し、各ステップで現在の最も古い値を最新の値で上書きし、毎回インデックスを循環的にステップします。

収集するデータを、ディスクに保存します。

環境によっては、経験豊富なヘルプを利用した方がよい場合もあります。大学では、コンピューターサイエンス学部の掲示板にメモを貼って、数時間の仕事に学生の賃金(または学生のコンサルティングレート)を提供して、データの処理を支援します。または多分あなたは学部の研究機会ポイントを提供します。か何か。

58
John R. Strohm

すべての問題は、間接レベルをさらに追加することで解決できます。そうする。

C++では配列の一部を削除できません。ただし、保持したいデータのみを保持する新しい配列を作成してから、古い配列を削除できます。そのため、不要な要素を前面から「削除」できるデータ構造を構築できます。実際に行うことは、新しい配列を作成し、削除されていない要素を新しい配列にコピーしてから、古い要素を削除することです。

または、std::deque、これはすでに効果的にこれを行うことができます。 deque、つまり「両端キュー」は、要素を一方の端から削除し、もう一方の端に要素を追加する場合を想定したデータ構造です。

13
Nicol Bolas

あなたが得たFIRとSMAの答えはあなたの場合には良いですが、私は機会を利用してより一般的なアプローチを推進したいと思います。

ここにあるのはstreamのデータです:一度にすべてのデータをメモリにロードする必要がある3つの大きなステップ(データの取得、計算、出力結果)でプログラムを構造化する代わりに、代わりに構造化できますパイプラインとして。

パイプラインは、ストリームから始まり、それを変換して、シンクにプッシュします。

あなたの場合、パイプラインは次のようになります。

  1. ディスクからアイテムを読み取り、一度に1つずつアイテムを放出する
  2. 一度に1つずつアイテムを受け取ります。受け取ったアイテムごとに、最後に受け取った5つ(循環バッファーが入ってくる場所)を放出します。
  3. 一度に5つずつアイテムを受け取り、グループごとに結果を計算します
  4. 結果を受け取り、ディスクに書き込みます

C++はストリームではなくイテレータを使用する傾向がありますが、正直に言うとストリームの方がモデル化が簡単です(ストリームに似たrangesの提案があります)。

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

そして、パイプラインは次のようになります。

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

ストリームは常に適用できるとは限りません(データへのランダムアクセスが必要な場合は機能しません)。ただし、ストリームは不安定です。非常に少量のメモリを操作することで、すべてをCPUキャッシュに保持します。


別のメモ:問題が「非常に並列」であるように見えるかもしれませんが、大きなファイルをチャンクに分割したい場合があります(5つのウィンドウで処理する場合は、各境界に4つの共通要素が必要であることに注意してください)。そして、チャンクを並行して処理します。

CPUが(I/Oではなく)ボトルネックである場合、ファイルをほぼ等量に分割した後、コアごとに1つのプロセスを起動することで、CPUを高速化できます。

4
Matthieu M.