2016-06-02 6 views
8

私は大きな3dスパース配列構造をメモリに無駄なくメモリに保持する方法を探しています。ここで私は、long型の配列を用いた実験を行ってきた:大きな配列はどのようにメモリを割り当てますか?

using System; 
using System.Diagnostics; 
using System.Runtime; 

namespace ConsoleApp4 
{ 
    public class Program 
    { 
     static Process proc = Process.GetCurrentProcess(); 
     const int MB = 1024 * 1024; 
     const int IMAX = 5; 
     const int JMAX = 100000000; 
     public static void ShowTextWithMemAlloc(string text) 
     { 
      proc.Refresh(); 
      Console.WriteLine($"{text,-30}WS64:{proc.WorkingSet64/MB,5}MB PMS64:{proc.PrivateMemorySize64/MB,5}MB"); 
      Console.ReadKey(); 
     } 
     public static void Main(string[] args) 
     { 
      Console.Write(" "); 
      ShowTextWithMemAlloc("Start."); 
      long[] lArray = new long[IMAX * JMAX]; 
      long[] l1Array = new long[IMAX * JMAX]; 
      long[] l2Array = new long[IMAX * JMAX]; 
      long[] l3Array = new long[IMAX * JMAX]; 
      ShowTextWithMemAlloc("Arrays created."); 
      lArray[IMAX * JMAX - 1] = 5000; 
      l1Array[IMAX * JMAX - 1] = 5000; 
      l2Array[IMAX * JMAX - 1] = 5000; 
      l3Array[IMAX * JMAX - 1] = 5000; 
      ShowTextWithMemAlloc("Last elements accessed."); 
      for (var i=IMAX-1; i>= 0; i--) 
      { 
       for (var j=0; j<JMAX; j++) 
       { 
        lArray[i * JMAX + j] = i * JMAX + j; 
       } 
       ShowTextWithMemAlloc($"Value for row {i} assigned."); 
      } 
      //lArray = new long[5]; 
      //l1Array = null; 
      //l2Array = null; 
      //l3Array = null; 
      //GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; 
      //GC.Collect(); 
      //ShowTextWithMemAlloc($"GC.Collect done."); 
      ShowTextWithMemAlloc("Stop."); 
     } 
    } 
} 

テストしたい場合は、COMPlus_gcAllowVeryLargeObjects環境変数を設定 - 1に(プロジェクトのプロパティ]> [デバッグ)またはJMAXを変更します。そして、これが出力されます。

Start.      WS64: 14MB PMS64: 8MB 
Arrays created.    WS64: 15MB PMS64:15360MB 
Last elements accessed.  WS64: 15MB PMS64:15360MB 
Value for row 4 assigned.  WS64: 779MB PMS64:15360MB 
Value for row 3 assigned.  WS64: 1542MB PMS64:15360MB 
Value for row 2 assigned.  WS64: 2305MB PMS64:15361MB 
Value for row 1 assigned.  WS64: 3069MB PMS64:15361MB 
Value for row 0 assigned.  WS64: 3832MB PMS64:15362MB 
Stop.       WS64: 3844MB PMS64:15325MB 

私はタスクマネージャでメモリ消費を参照してくださいProcess.WorkingSet64でこのようなものです。実数は何ですか?割り当てにメモリが割り当てられるのはなぜですか?配列は実際に連続して割り当てられたメモリですか?配列は配列ですか?エイリアンは存在しますか? (劇的なバックグラウンドミュージック)

エピソード2:

  //lArray[i * JMAX + j] = i * JMAX + j; 
      var x= lArray[i * JMAX + j]; 

と何も変化(出力中): 私たちは、小さな変更を加えます。存在と非存在の違いはどこですか? (より劇的なバックグラウンドミュージック)今、私たちは神秘的な人々のひとり(彼らの名前の下にいくつかの数字と小さな「k」がある)からの答えを待っている。つのアレイ(15369から8)/ 4 = 3840メガバイト これは疎な配列ではなく、一部のため

Start.        WS64: 14MB PMS64: 8MB 
Arrays created.     WS64: 15MB PMS64:15369MB 
20000000 values assigned.   WS64: 168MB PMS64:15369MB 
Stop.        WS64: 168MB PMS64:15369MB 

PMS64:

エピソード3: 別の変更:

//lArray[IMAX * JMAX - 1] = 5000; 
    //l1Array[IMAX * JMAX - 1] = 5000; 
    //l2Array[IMAX * JMAX - 1] = 5000; 
    //l3Array[IMAX * JMAX - 1] = 5000; 
    //ShowTextWithMemAlloc("Last elements accessed."); 
    long newIMAX = IMAX-3; 
    long newJMAX = JMAX/10; 
    for (var i=0; i<newIMAX; i++) 
    { 
     for (var j=0; j<newJMAX; j++) 
     { 
      lArray[i * newJMAX + j] = i * newJMAX + j; 
      //var x= lArray[i * JMAX + j]; 
     } 
     //ShowTextWithMemAlloc($"Value for row {i} assigned."); 
    } 
    ShowTextWithMemAlloc($"{newIMAX*newJMAX} values assigned."); 

出力私はこの168MBをフルに使っています。

「なぜあなたは正確なサイズを使用しませんか?」という質問に答えます。私はそれを知らないので?データは、複数のユーザー定義SQLから取得できます。なぜサイズを変更しないのですか? " Resizeは新しい配列を作成し、値をコピーします。これはコピー、記憶の時間であり、最終的に悪いGCが来てあなたを食べる。

メモリを浪費しましたか? (私は覚えていない。宇宙人?!) そして、はい、どのくらい? 0、(3840-168)MBまたは(15369-8-168)MB?

エピローグ:

コメントはコメントか回答ですか?

は実際に連続したメモリですか?

答えが答えますか?神秘的な。 (more music

スカリー:モルダー、ヒキガエルはちょうど空から落ち モルダー:!。私は彼らのパラシュートが開かなかったと思います)

はあなたのすべてをありがとう!

+3

*配列は実際に連続して割り当てられたメモリですか?配列は配列ですか?宇宙人は存在するのですか?*はい。はい。おそらく、彼らは遠くにいる*。 –

+1

あなたが逃したかもしれない(実際に面白い)質問は、「メモリは実際にメモリですか?」と疑います。 ...そしてたぶん「大丈夫ですが、メモリがメモリの場合、連続したメモリは実際に連続したメモリですか?」 - いいえ、答えを書くのにこれについて十分なことは分かりません。 – moreON

答えて

6

作業セットは、割り当てられたメモリの量ではありません。これは、プロセスで現在利用可能な一連のページです。 Windowsでは、さまざまなポリシーが実装されており、一般的に解釈するのは難しいです。

ここでは、メモリはOSからゼロになっている可能性があります。ページへの最初のアクセスでは、実際にゼロページが利用可能になります。

プライベートバイトが必要です。

.NET配列をまばらに割り当てることはできません。おそらく、希薄な配列の印象を提供するいくつかのデータ構造を採用することを検討する必要があります。

アレイは実際に連続して割り当てられたメモリですか?

はい、CLRと.NETコードが実行されています。 OSは、最初の読み取りまたは書き込み時にページに怠惰なフォールトを起こすなどのトリックを行うことがあります。

「エピソード2」では、読み込みと書き込みの両方でフォールトが発生するという答えがあります。私はエピソード3のことにはほとんど従っていませんが、ページ数を減らすと仮定しています。

私は、これは言って、より複雑であるメモリ

を無駄にしました。ページに触れていない限り、物理的には使用されていません。それらは、例えば、ファイルキャッシュのために、または他のプログラム常駐作業セットのために使用することができる。しかし、彼らはシステムのコミット料金に数えます。 Windowsは、それらのページを利用できるようにすることを保証します。ランダムなメモリアクセスでメモリが不足することはありません。 Linuxはこれを保証するものではありません。それは緩和としてOOMキラーを持っています。

極端な場合は、1TBを割り当てると、RAMとページングファイルのサイズの合計が1TBを超える必要がありますが、そのスペースが使用されなくなる可能性があります。

メモリマップファイルの使用を検討してください。ここで、ファイルはバッキングストアであり、RAMはキャッシュのように扱われます。これはまったく同じように動作します。

関連する問題