2016-06-02 20 views
8

Poszukuję sposobu na przechowywanie dużej, przestrzennej struktury tablic w pamięci bez marnowania dużej ilości pamięci. Tutaj zrobiłem eksperyment z tablic tęskni:W jaki sposób duża tablica przydziela pamięć?

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

Jeśli chcesz przetestować go ustawić zmienną środowiskową COMPlus_gcAllowVeryLargeObjects (Projekt Właściwości -> Debug) na 1 lub zmienić jMax. I to jest wyjście:

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 

Kiedy widzę zużycie pamięci w Menedżerze zadań jest tak w Process.WorkingSet64. Jaka jest rzeczywista liczba? Dlaczego pamięć jest przydzielana do zadania? Czy tablica jest faktycznie ciągłą przydzieloną pamięcią? Czy tablica jest tablicą? Czy kosmici istnieją? (Dramatyczna muzyka w tle)

Odcinek 2: Wykonujemy małe zmiany:

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

i nic zmianę (na wyjściu). Gdzie jest różnica między istniejącym a nieistniejącym? (bardziej dramatyczna muzyka w tle) Teraz czekamy na odpowiedź od jednego z tajemniczych ludzi (mają oni pewną liczbę i małe "k" pod swoimi nazwiskami).

Część 3: Kolejna zmiana:

//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."); 

Wydajność:

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

PMS64 do jednej tablicy (15369-8)/4 = 3840MB Nie jest to rzadki tablicy, a częściowo wypełniona tablica;). Używam pełnego tego 168 MB.

Odpowiedz na pytanie "Dlaczego nie używasz dokładnego rozmiaru?". Bo ja tego nie wiem? Dane mogą pochodzić z kilku SQL zdefiniowanych przez użytkownika. "Dlaczego nie zmieniasz rozmiaru?". Resize tworzy nową tablicę i kopiuje wartości. To jest czas na kopiowanie, pamięć i na końcu przychodzi zły zło i jedzą.

Czy marnowałem pamięć. (Nie pamiętam, kosmici ?!) A kiedy tak, ile? 0, (3840-168) MB lub (15369-8-168) MB?

Epilog:

jest komentarz komentarz lub odpowiedź?

jest ciągłą pamięcią sąsiadującą pamięć?

Czy odpowiedzi dają odpowiedzi? Tajemniczy. (more music)

(Scully: Mulder, ropuchy po prostu spadł z nieba Mulder!. Chyba ich spadochrony nie otwierają)

Dziękuję wszystkim!

+3

* Czy tablica faktycznie jest ciągłą przydzieloną pamięcią? Czy tablica jest tablicą? Czy kosmici istnieją? * Tak. Tak. Prawdopodobnie, ale są * daleko *. –

+1

Podejrzewam, że (faktycznie interesujące) pytanie, które mogłeś przegapić, brzmi: "Czy pamięć to pamięć"? ... i może "okej, ale kiedy pamięć jest pamięcią, sąsiadująca pamięć jest faktycznie ciągłą pamięcią?" - Nie, naprawdę nie wiem wystarczająco dużo na ten temat, aby napisać odpowiedź. – moreON

Odpowiedz

6

Zestaw roboczy nie jest ilością przydzielonej pamięci. Jest to zestaw stron, które są obecnie dostępne dla procesu. Windows wdraża różne strategie w tym zakresie, a ich liczba jest trudna do zinterpretowania.

Tutaj pamięć prawdopodobnie została zażądana jako zerowana z systemu operacyjnego. Pierwszy dostęp do strony powoduje, że strona zerowana jest dostępna.

Powinieneś patrzeć na prywatne bajty.

Nie można rzadko przydzielać tablic .NET. Prawdopodobnie powinieneś popatrzeć na użycie struktury danych, która daje wrażenie nieskomplikowanej tablicy.

Czy tablica jest rzeczywiście ciągłą przydzieloną pamięcią?

Tak, z perspektywy CLR i uruchomionego kodu .NET. System operacyjny może odgrywać takie sztuczki, jak leniwe błędy na stronach pierwszego odczytu lub zapisu.

Dla "Epizodu 2" odpowiedź brzmi, że błędy dotyczą zarówno odczytów, jak i zapisów. Nie śledzę dokładnie tego, co robi odcinek 3, ale zakładam, że dotyka tylko mniej stron.

Czy tracę pamięć

To jest bardziej skomplikowane, aby powiedzieć. Dopóki strony nie zostaną dotknięte, nie są fizycznie używane. Można je wykorzystać do przechowywania plików na przykład lub do innego zestawu roboczego rezydentnego innego programu. Jednak liczą się do opłaty za zatwierdzenie systemu. System Windows gwarantuje, że może udostępnić te strony. Nie zabraknie ci pamięci przy jakimś przypadkowym dostępie do pamięci. Linux nie gwarantuje tego. Ma zabójcę OOM jako narzędzie łagodzące.

W skrajnym przypadku, jeśli przydzielisz 1 TB tak samo, potrzebujesz sumy pamięci RAM i rozmiaru pliku stronicowania, aby przekroczyć 1 TB, nawet jeśli żadna z tych powierzchni nie zostanie użyta.

Należy rozważyć użycie plików mapowanych w pamięci. Tutaj plik jest zapleczem, a pamięć RAM jest traktowana jak pamięć podręczna. To zachowałoby się dokładnie w ten sam sposób.

Powiązane problemy