メモリ使用量の多いアプリケーションでのメモリ不足を回避するにはどうすればよいですか?C / C++

StackOverflow https://stackoverflow.com/questions/741834

  •  09-09-2019
  •  | 
  •  

質問

openstreetmap XML ファイルを取得し、通常は元のサイズの約 10% のバイナリ ランタイム レンダリング形式に変換するコンバータを作成しました。入力ファイルのサイズは通常 3GB 以上です。入力ファイルは一度にメモリにロードされるのではなく、ポイントとポリゴンが収集されるごとにストリーミングされ、それらに対して BSP が実行されてファイルが出力されます。最近、大きなファイルではメモリ不足で動作しなくなりました (問題のファイルには 1,400 万ポイントと 100 万ポリゴンがあります)。通常、この問題が発生すると、プログラムは約 1 GB ~ 1.2 GB の RAM を使用します。仮想メモリを 2 から 8 GB (XP の場合) に増やそうとしましたが、この変更は効果がありませんでした。また、このコードはオープンソースであるため、利用可能な RAM に関係なく (低速ではありますが) 動作し、Windows、Linux、Mac 上で動作するようにしたいと考えています。

メモリ不足を回避するにはどのようなテクニックを使用できますか?データをより小さなサブセットで処理して、最終結果をマージしますか?独自の仮想メモリ タイプのハンドラーを使用していますか?他に何かアイデアはありますか?

役に立ちましたか?

解決

まず、32 ビット システムでは、ページファイルの設定に関係なく、メモリは常に 4 GB に制限されます。(そのうち、Windows 上のプロセスで使用できるのは 2GB のみです。Linux では、通常約 3GB が使用可能です)

したがって、最初の明らかな解決策は、64 ビット OS に切り替えて、アプリケーションを 64 ビット用にコンパイルすることです。これにより、使用できる巨大な仮想メモリ空​​間が得られ、OS は動作を維持するために必要に応じてページファイルとの間でデータを交換します。

次に、一度に割り当てるメモリのチャンクを小さくすると効果がある可能性があります。多くの場合、1 GB の空きメモリ チャンクを 1 つ見つけるよりも、256 MB の空きメモリ チャンクを 4 つ見つける方が簡単です。

第三に、問題を分割します。データセット全体を一度に処理するのではなく、一度に小さなセクションのみをロードして処理するようにしてください。

他のヒント

あなたはどこにでもメモリリークされていないことを保証するためにチェックしましたか?

あなたのプログラムは、Linuxへの移植性があるので、

、私はValgrindの下でそれを実行すると確認することをお勧めます。

すでにやっているようですね サックス XML 処理に対するベースのアプローチ (一度にすべてではなく、進行中に XML をロードします)。

ほとんどの場合、解決策はアルゴリズムを変更して問題をより小さな部分に分割することです。物理的に一度に多くのメモリを割り当てず、必要なものだけを読み取り、処理してから書き込みます。

アルゴリズムで必要な場合は、代わりにハード ドライブを使用してメモリを拡張できる場合があります。

アルゴリズムを分割できない場合は、次のようなものが必要になるでしょう。 メモリマップされたファイル.

最悪の場合は、次のようなものを使用してみることができます 仮想アロック Windows システムを使用している場合。32 ビット システムを使用している場合は、次のようなものを使用してみてください。 物理アドレス拡張 (PAE).

プログラムに入力制限を設け、32 ビット システムと 64 ビット システムで異なる制限を設けることも検討できます。

私はあなたのメモリの問題がメモリにBSPツリーを維持するからです疑います。だから、ディスク上のBSPを維持し、メモリ内にのみいくつかのチャンクを保ちます。構造は、いくつかの他のツリー構造よりも多くの向いているように、これは、BSPとかなり簡単であるべきであり、ロジックはシンプルでなければなりません。効率的かつメモリ優しいの両方であるために使用可能なメモリーより少なく余裕のビットに設定されたキャッシュサイズと、ダーティフラグ/ wのキャッシュを持っている可能性があります。

あなたは自分のメモリ制限を超えてのみであり、上記の提案として、あなたの<のhref = "HTTPに/ 3GBスイッチを追加することができ、コードを手直しする時間を望むか、持っていない場合は、

、Windows XPを使用していると仮定すると、 ://technet.microsoft.com/en-us/sysinternals/bb963892.aspx」のrel = 『nofollowをnoreferrer』> boot.iniファイルのファイルと、それ余分な1ギガバイトを取得するためのリンカスイッチを設定するだけの問題メモリのます。

あなたは実メモリが(Windowsでのワーキングセットと呼ばれる)のメモリつまりながら使用している仮想メモリの量は、あなたが予約してきた合計金額であるという点で仮想メモリが「RAM」とは異なることを理解する必要がありますあなたは、実際に変更またはロックされてきました。

他の誰かが指摘したように、あなたが3ギガバイトのための特別なフラグを設定し、すべてのポインタのコードで、あなたが使用のみを使用するすべてのライブラリの両方ことを確認することができない限り、

、32ビットWindowsプラットフォーム上で仮想メモリの上限は2GBです符号なしのポインタ。

だから、どちらか64ビットにユーザーを強制したり、仮想メモリを監視し、快適に私のアドバイスになり、32ビットオペレーティングシステムによる制限内に収まるものにあなたの最大ブロックサイズをキャッピング。

私は、Windowsの32ビットの壁に激突してきましたが、Linuxでこれらの制限を回避作業経験がないので、私は唯一のものの、Windowsの側面について話しました。

32ビットXP上の最大のプログラムのアドレス空間は2GBです。そして、あなたが原因DLLのと自分のアドレス空間にして、最大ロードドライバに断片化しています。最後に、あなたのヒープの断片化の問題を持っています。

あなたの最善手はただでそれを乗り越えると(64ビットシステム上の)64ビットプロセスとして実行することです。突然、これらの問題はすべて消えます。あなたは、ヒープの断片化の影響を緩和するために、より良いヒープを使用することができ、あなたは一つの大きな連続した塊にあなたの記憶をつかむためにVirtualAllocのを使用して試すことができます(そして、あなたはそこからそれを管理するために得る!)それを断片化からDLLの/ドライバを阻止します。

最後に、あなたがプロセス間あなたのBSPを分割することができます。複雑で、痛みを伴う、と率直にちょうどディスク上に置くことは容易になるだろうが、理論的には、あなたが居住者すべてを保つことができれば、情報を交換するプロセスのグループを持つ(とあなたがOSよりもメモリよりも賢くすることができ想定して、より良いパフォーマンスを得ることができますファイルのバッファリング...)もし大きい扱うことができます。各プロセスははるかに少ないメモリを必要とするので、2GBのアドレス空間の制限に実行しないでください。もちろん、あなたはたくさんの速いRAM /スワップを通じて燃やすでしょう。

あなたは小さなチャンクを割り当てることにより、アドレス空間の断片化の影響を軽減することができます。これは、他の厄介な副作用がありますが、あなたは成功した割り当てに失敗する場合は、メモリのますます小さいチャンクをつかむバックオフ方針に従うことができます。頻繁にこの単純なアプローチはあなたにそれがそうではないだろう動作しますが、残りの時間は、同様にそれができたとして実行するプログラムを取得します。

少年は、64ビットコンピューティングは、単に他の選択肢よりも、そんなによりよい音ではありません?

どのようにポイントにメモリを割り当てていますか?あなたが一度にポイント1を割り当てている(例えば、 PT =新しいポイント)。そして、ポイントのサイズに応じて、いくつかのメモリが無駄になることがあります。たとえば、Windowsのメモリに16バイトの倍数に割り当てられているので、あなたは、1つのバイトを割り当てようとお願いした場合でも、OSが実際に16のバイトを割り当てます。

この場合、

、メモリアロケータを使用することを助けることができます。あなたは、STLのアロケータを使用して簡単なチェックを行うことができます。 (オーバーPointクラスのための新しい演算子をロードし、むしろ「malloc関数」よりメモリを割り当てるか、new演算子をデフォルトにSTLアロケータを使用)。

あなたは割り当て、最適な方法でメモリの割り当てを解除することはできません。他の人が指摘したように、あなたはメモリをリークし、それを知らないことができます。メモリ割り当てをデバッグと最適化には時間がかかります。

あなたはメモリ使用量を最適化する時間を費やすしたくない場合は、なぜ<のhref =「http://www.hpl.hp.com/personal/Hans_Boehm/gc/」のrel =「nofollowをnoreferrer」を試してみて>保守的なガベージコレクタの?それは)(mallocのためのプラグインの代替だ/新と自由()。実際には、無料()何もしませんので、あなたは自分のプログラムからこれらの呼び出しを削除することができます。以前には示唆されているように、代わりに、あなたはあなたのプログラムを手で最適化し、メモリのプールを管理している場合は、CGCは、すでにあなたのためのない多くの仕事をやってしまいます。

あなたはあなたの出力だけでなく、あなたの入力をストリーミングする必要があります。あなたの出力形式は、ストリーム指向でない場合は、第二のパスを行うことを検討。例えば、出力ファイルは、データのチェックサム/サイズで始まる場合、最初のパスでスペースを残し、後でそのスペースへの書き込み/模索ます。

あなたがバイナリ会話にtxtをやっているように、

これは聞こえるので、なぜあなたはメモリ内の全データを持っている必要がありますか?。
あなただけの、その後のBinaryStreamする保存TXT(XML)からプリミティブを読むことができませんか?

あなたは、メモリサイズに依存しないようにしたい場合は、

は、サイズに依存しないアルゴリズムを必要としています。あなたがコントロール下にメモリ使用量を持っていない場合はどんなにあなたのRAMがどのようなサイズ、あなたが境界線にぶつかるするつもりはありませんしています。

あなたはおそらく出力のビットを生成するために使用できる情報の少なくともチャンクを見てみましょう。そして、このサイズのチャンクに入力を分割する方法を考えます。

さて、それはそれは、簡単ではありません聞こえますか? (グラッド私は:)それを行う必要はありません)。

64 ビット マシンに切り替える必要はありません。また、他の人が提案した 1000 の機能のほとんども必要ありません。必要なのは、より思慮深いアルゴリズムです。

この状況を解決するためにできることは次のとおりです。

  • Windows を使用している場合は、ファイル マップ (サンプルコード)。これにより、実際にそれを行わずに、あたかもメモリ内のファイル全体を読み取るかのように、単一のバッファ ポインタを介してファイルにアクセスできるようになります。Linux カーネルの最近のバージョンには同様のメカニズムがあります。
  • 可能であれば、可能であれば、ファイルを順番にスキャンして、メモリ内 DOM の作成を避けてください。これにより、ロード時間とメモリ要件が大幅に軽減されます。
  • プールされたメモリを使用してください!おそらく、ノード、ポイントなどの小さなオブジェクトがたくさんあるでしょう。これを助けるためにプールされたメモリを使用してください (アンマネージ言語を使用していると想定しています。プールされた割り当てとメモリ プールを検索します)。
  • マネージ言語を使用している場合は、少なくともこの特定の部分をアンマネージ言語に移動し、メモリとファイルの読み取りを制御します。マネージ言語には、メモリ使用量とパフォーマンスの両方において少なからぬオーバーヘッドがあります。(はい、これに「C++」のタグが付いていることは知っています...)
  • 一度に最小限のデータのみを読み取って処理するインプレース アルゴリズムの設計を試みると、メモリ要件が軽減されます。

最後に、複雑なタスクには複雑な対策が必要であることを指摘しておきます。8 GB の RAM を搭載した 64 ビット マシンを購入できると考えられる場合は、完了までに 1 日かかっても、「ファイルをメモリに読み取り、データを処理し、出力を書き込む」アルゴリズムを使用してください。

そのための良い技術があります、ファイルの中にいくつかのインスタンスを格納することで、それらを取得した後、あなたがそれらを使用する必要があるときます。

この技術は、メモリの大きな量が必要なときにスケーラブルになるようにDoxygenのような多くのオープンソースソフトウェアで使用されます。

私は最近、同じことをやったので、

これは、古い質問ですが....

簡単な答えはありません。理想的な世界では、巨大なアドレス空間(すなわち64ビット)、および物理メモリの膨大な量のマシンを使用すると思います。一人で巨大なアドレス空間は十分ではないか、それだけでスラッシュます。その場合、データベースにXMLファイルを解析し、適切なクエリで、あなたが必要なものを引き出します。かなり可能性がこれは(私は世界で330ギガバイト程度であると考えている)OSM自身が何をするかです。

実際には、私はまだ便宜の理由のためにXPの32ビットを使用しています。

これは、スペースと速度のトレードオフです。あなたはそれにかかる時間を気にしない提供するメモリの任意の量でほとんど何でも行うことができます。 STL構造を使用して、あなたが欲しいものを解析することができますが、すぐにメモリが不足します。あなたは交換し、独自のアロケータを定義することができますが、マップ、ベクトル、セットなどは本当にあなたが何をしているかわからないので、再び、それは非効率的になるでしょう。

私は32ビットマシン上でそれを小さなフットプリントですべての作業をするために見つけた唯一の方法は、非常に慎重にするときに必要だったものを私がやっていたし、何について考えると、チャンクにタスクを分割することでした。メモリ効率的な(〜の100メガバイト以上のものを使用することはありません)が、大規模に迅速ではないが、それは問題ではありません - どのように多くの場合、1つは、XMLデータを解析する必要がない。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top