3

double の非常に大きな配列をいくつか使用する C++ プログラムがあり、プログラムのこの特定の部分のメモリ フットプリントを削減したいと考えています。現在、100 個を割り当てており、それぞれ 100 Mb にすることができます。

現在、これらの配列の一部は、プログラムの実行の後半で最終的に時代遅れになり、一度にいずれかの配列全体をメモリに保持する必要はほとんどないという利点があります。

私の質問はこれです:

new または malloc で配列を作成した後で、その一部が不要になったことを OS に伝える方法はありますか? これを達成する唯一の方法は、ポインターの配列を宣言することであるという結論に達しています。各ポインターは、目的の配列の 1Mb などのチャンクを指している可能性があるため、古いチャンクはもう必要ありません。配列の新しいビットに再利用できます。これは、ちょっとしたハンマーのように見えるカスタム メモリ マネージャーを作成するようなものに思えます。

スレッド競合の問題が多すぎるため、配列内のデータを移動できません。配列には、多数のスレッドのいずれかがいつでもアクセスできますが、特定の配列に書き込むスレッドは 1 つだけです。

4

5 に答える 5

4

オペレーティングシステムによって異なります。POSIX (Linux を含む)には、madviseメモリ パフォーマンスを向上させるためのシステム コールがあります。マニュアルページから:

madvise() システム コールは、アドレス addr で始まる size length バイトのアドレス範囲でのページング入出力の処理方法についてカーネルにアドバイスします。これにより、アプリケーションは、カーネルが適切な先読みおよびキャッシュ技術を選択できるように、マップされたメモリ領域または共有メモリ領域の使用方法をカーネルに伝えることができます。この呼び出しは、アプリケーションのセマンティクスには影響しませんが (MADV_DONTNEED の場合を除く)、パフォーマンスに影響を与える可能性があります。カーネルはアドバイスを自由に無視できます。

詳細については、のマニュアル ページを参照してmadviseください。

編集:どうやら、上記の説明は十分に明確ではありませんでした。そのため、ここにいくつかの詳細を示します。そのうちのいくつかは Linux に固有のものです。

を使用mmapして、ファイルによってサポートされていないメモリ ブロックを (libc ではなく OS から直接) 割り当てることができます。メモリの大きなチャンクの場合、mallocまったく同じことを行っています。munmapの使用に関係なく、メモリを解放するために使用する必要がありmadviseます。

void* data = ::mmap(nullptr, size, PROT_READ | PROT_WRITE,
    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// ...
::munmap(data, size);

このチャンクの一部を取り除きたい場合は、 を使用madviseしてカーネルにそうするように指示できます。

madvise(static_cast<unsigned char*>(data) + 7 * page_size,
    3 * page_size, MADV_DONTNEED);

アドレス範囲は引き続き有効ですが、物理 RAM にもストレージにもバックアップされなくなりました。後でページにアクセスすると、カーネルはその場でいくつかの新しいページを割り当て、それらをゼロに再初期化します。Dontneedページもプロセスの仮想メモリ サイズの一部であることに注意してください。オーバーコミットの有効化など、仮想メモリ管理にいくつかの構成変更を加える必要がある場合があります。

于 2012-04-20T11:26:37.760 に答える
1

詳細があれば答えやすいでしょう。

1°)「newまたはmallocでアレイを作成した後、その一部が不要になったことをOSに伝える方法はありますか?」という質問への回答。「そうではない」です。これがCとC++のポイントであり、メモリを手動で処理できる言語です。

2°)CではなくC ++を使用している場合は、mallocを使用しないでください。

3°)非常に特別な理由がない限り、アレイもありません。std::vectorを使用します。

4°)できれば、配列のコンテンツを頻繁に変更してメモリフットプリントを削減する必要がある場合は、リンクリスト(std :: list)を使用しますが、リストのコンテンツに個別に「アクセス」する方がコストがかかります。 (ただし、それを繰り返すだけの場合は、ほぼ同じくらい高速になります)。

于 2012-04-20T11:28:46.783 に答える
1

std::dequeポインタ付きのAstd::array<double,LARGE_NUMBER>がその役割を果たしますが、dequeを使用して専用のコンテナを作成する方がよいため、インデックスを再マップし、最も重要なこととして、エントリが使用されなくなった時期を定義できます。

専用コンテナには読み取り/書き込みロックを含めることもできるため、スレッドセーフな方法で使用できます。

于 2012-04-20T11:29:53.693 に答える
0

途中でチャンクとdelete[]-ing とnew[]-ing で割り当てるのが良い解決策のようです。メモリ管理を最小限に抑えることができる場合があります。チャンクを自分で再利用しないでください。単に古いチャンクの割り当てを解除し、必要に応じて新しいチャンクを割り当ててください。

于 2012-04-20T11:51:22.743 に答える
0

配列の代わりにリストを使用してみてください。もちろん、リストは配列よりも「重い」のですが、一方で、リストが古くなったときにその一部を捨てることができるように、リストを再構築するのは簡単です。リストのどの部分が最新で、どの部分が再利用できるかを示すインデックスのみを含むラッパーを使用することもできます。これにより、パフォーマンスが向上しますが、(再利用可能な) メモリが少し多く必要になります。

于 2012-04-20T11:27:43.827 に答える