none
オブジェクトをキャッシュする RRS feed

  • 質問

  • 巨大なオブジェクトをキャッシュすることでメモリ使用量を低減させたい。検討する価値があるFCLをアドバイス願います。

    前提
    (1)32bit版WindowsXPでの使用が前提である。(64bit対応は検討から除外)
    (2).NET Framework 4.0が使用できる。
    (3)数値解析で巨大な行列(中身はfloat[16000,1000]≒256MB)を3つ使用している。
    (4)上記配列をオンメモリで配置するとプロセスのワーキングセットが1.2GBに達し、32bitWindowsではメモリ不足が発生する。
    (5)ランダムアクセスではなく行ごとのシーケンシャル処理が多いのでオブジェクト全体はディスク等に退避し、一部のみメモリにキャッシュするのは良いアイデアと思われる。(6)(5)の実装を数値解析ルーチンから隠蔽したい。
    (7)(5)の実装(メモリへの読込み、退避等)の手を抜きたい。できれば自動でやってほし
    当然のことではあるが、
    (8)メモリにキャッシュされている部分へのアクセスは高速であること。
    (9)退避されている部分がワーキングセットに加算されないこと。

    下記の手法はすでに検討済みです。
    ・OSレベルの対応(3GBオプション、LAAフラグ=ON)
    ・弱い参照によるメモリ消費量の最適化。(GCへの参照が残っているオブジェクトの復活させることでキャッシュに似た働きをさせる)

    以上


    C#開発者
    2011年10月5日 3:49

回答

  • 外池と申します。

    前提条件がよくわからないです。16000×1000 の行列で 256 MByteになります? float? double? 64 あるいは 128 MByteにしかならないのではないかと・・・。

    あと、束縛条件がどれだけあるのかがはっきりしませんが、私なら、まずは、OSに任せますね。下手にプログラムしたらかえって遅くなりそうで。

    さらに、4GByteのメモリを搭載して、そのうち1GByteとか、1.5GByteとか、量を指定してメモリーを仮想HDとして割り当てるソフトがあると思います。その仮想HDにOSのキャッシュを置くように指定すれば、かなり速いと思いますよ? まぁ、これだと、作っておられるソフトを動かす環境を、特別に整備しないといけませんが。


    (ホームページを再開しました)
    • 回答としてマーク MicroVAX 2014年11月28日 4:43
    2011年10月5日 6:00
  • 普通ワーキングセットが大きくなってもメモリ不足にはならない(表現が微妙ですが)と思うのですが、メモリ不足とは具体的に何を意味しているでしょうか?

    もし、OutOfMemoryExpcetionが発生するということであれば、問題はワーキングセットではなくおそらく.NET側のラージメモリヒープの断片化による確保失敗のような気がしますが、この場合は最初の確保時点でエラーになるはずですのでちょっと分からないですね…

    OutOfMemoryExceptionが出るようでしたら、配列を行単位などで別々に確保(2次元配列ではなくジャグ配列のような形)してみてください。
    配列アクセスが元々局所化されているなら、Windowsが勝手にうまいことワーキングセットを調整してくれます。
    ワーキングセットサイズをAPIで小さめに設定してもいいかもしれませんが。

     

    • 回答としてマーク 山本春海 2011年10月26日 8:05
    2011年10月5日 7:57
  • すでに言われていることの繰り返しになってしまいますが、LOH(Large Object Heap) はコンパクション(領域を詰めて空き領域を広げる)が実行されないので、断片化するような使い方をしていれば、大きな領域の一括確保はできなくなることがあります。
    もっとも、仮想メモリ空間は断片化するという意味では、マネージヒープに限らない話ですが、ガベージコレクタ任せのマネージヒープではより問題を引き起こしやすい印象です。(絶対ではありません。あくまで私見です)

    誰にでも適用可能な、かんたんな対策はありません。
    .NET のメモリ管理の仕組みを学びつつ、メモリ周りの設計を大幅に見直すことが必要になるでしょう。
    (アプリケーションごとに解決のアプローチが異なるので、一律にいえることは残念ながらありません)


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年10月6日 13:56
    モデレータ

すべての返信

  • 外池と申します。

    前提条件がよくわからないです。16000×1000 の行列で 256 MByteになります? float? double? 64 あるいは 128 MByteにしかならないのではないかと・・・。

    あと、束縛条件がどれだけあるのかがはっきりしませんが、私なら、まずは、OSに任せますね。下手にプログラムしたらかえって遅くなりそうで。

    さらに、4GByteのメモリを搭載して、そのうち1GByteとか、1.5GByteとか、量を指定してメモリーを仮想HDとして割り当てるソフトがあると思います。その仮想HDにOSのキャッシュを置くように指定すれば、かなり速いと思いますよ? まぁ、これだと、作っておられるソフトを動かす環境を、特別に整備しないといけませんが。


    (ホームページを再開しました)
    • 回答としてマーク MicroVAX 2014年11月28日 4:43
    2011年10月5日 6:00
  • たとえ256MBだったとしても 256MB x 3 + α でも 1.2GB にはならないし、1.2GBだったとしても2GB or 3GBのメモリ不足にはならないし、ほんとどういう状況なんでしょうね。

    普通にページファイルを割り当てればいいかと思います。

    それと別の観点で、 float[16000,1000] x 3 よりも "float x 3" の構造体配列にした方が、メモリアクセスの局所性とかなんとか。行内でシーケンシャルアクセスなだけで行間でランダムアクセスならこれは意味を持ちませんが。

    2011年10月5日 6:14
  • 上がっている数字はさておき、(ヒープにメモリを確保できるか、ヒープが拡張できるかは、微妙な問題なので) 解決策としては、ディスク上の内容をメモリ上にあるかのように扱える Memory Mapped File を使うのが一番手軽だとは思います。使い方にもよりますが、搭載されているメモリ量にあわせて読み込みはメモリへキャッシュされるように OS が制御してくれます。

    個人的には、MemoryStream を拡張したマップメモリストリームを持っているので、ものの数分でそういうものができちゃうのですが、ゼロから作る場合は .NET4 だと System.IO.MemoryMappedFiles namespace でサポートが入っていますので、そちらをつかって楽ができるかもしれません。(使ったことがないので詳細は不明)

    ざっくりとは、ディスク上に必要なサイズを確保しておいて、そのオブジェクトに配列であるかのように扱えるインデクサを作成し、指定されたインデックス値にあわせてメモリアドレスの相対位置を計算して読み書きする処理を容易するだけです。その他、System.IO.MemoryMappedFiles 下のライブラリがどこまで処理してくれるかわかりませんが、仮想メモリ空間を使い切らないようなページング処理をサポートしていない場合は、それも実装が必要になります。

     


    2011年10月5日 7:30
  • 普通ワーキングセットが大きくなってもメモリ不足にはならない(表現が微妙ですが)と思うのですが、メモリ不足とは具体的に何を意味しているでしょうか?

    もし、OutOfMemoryExpcetionが発生するということであれば、問題はワーキングセットではなくおそらく.NET側のラージメモリヒープの断片化による確保失敗のような気がしますが、この場合は最初の確保時点でエラーになるはずですのでちょっと分からないですね…

    OutOfMemoryExceptionが出るようでしたら、配列を行単位などで別々に確保(2次元配列ではなくジャグ配列のような形)してみてください。
    配列アクセスが元々局所化されているなら、Windowsが勝手にうまいことワーキングセットを調整してくれます。
    ワーキングセットサイズをAPIで小さめに設定してもいいかもしれませんが。

     

    • 回答としてマーク 山本春海 2011年10月26日 8:05
    2011年10月5日 7:57
  • ご回答ありがとうございます。

    32bit版Windowsで動かすとOutOfMemoryExceptionが発生します。64bit版Windowsでは発生しません。今回はターゲットのOSが32bitなので32bit版でOutOfMemoryExceptionが発生しないようにしなくてはなりません。RAM=4GB/スワップファイル=4GBの環境です。
    オブジェクトが使用済みになったらこまめに参照を外しています。また、大きなオブジェクトを解放した直後には、明示的にGC.Collect()を入れてみる。といった対処を試みましたがあまり効果がありませんでした。オンメモリでオブジェクト全体を読込むのでは限界があると考え質問しました。

    System.IO.MemoryMappedFiles←このような名前空間があるとは知りませんでした。


    C#開発者
    2011年10月6日 11:02
  • すでに言われていることの繰り返しになってしまいますが、LOH(Large Object Heap) はコンパクション(領域を詰めて空き領域を広げる)が実行されないので、断片化するような使い方をしていれば、大きな領域の一括確保はできなくなることがあります。
    もっとも、仮想メモリ空間は断片化するという意味では、マネージヒープに限らない話ですが、ガベージコレクタ任せのマネージヒープではより問題を引き起こしやすい印象です。(絶対ではありません。あくまで私見です)

    誰にでも適用可能な、かんたんな対策はありません。
    .NET のメモリ管理の仕組みを学びつつ、メモリ周りの設計を大幅に見直すことが必要になるでしょう。
    (アプリケーションごとに解決のアプローチが異なるので、一律にいえることは残念ながらありません)


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年10月6日 13:56
    モデレータ