Mam pytanie odnośnie wykorzystania całych zasobów pamięci ram przez aplikację napisaną w Visual C# 2010 Express.
Wykorzystuje ona “Dictionary” do przechowywania pewnych danych.
Posiadam Windows 7 x64 i 4GB ram - w wersji 32bitowej aplikacji maksymalnie mogłem dodać około 45mln rekordów do słownika (~1.6GB ramu zajmował proces). Postanowiłem przekompilować do x64 - udało się “wcisnąć” 60mln rekordów (~2.1GB) i dalej nie chce już iść krzycząc że brakuje pamięci…
Postanowiłem więc uruchomić aplikację na kompie z 8GB pamięci i o dziwo ta sama sytuacja co przy 4GB - jedynie 60mln wchodzi do słownika…
Coś wygooglowałem o stosowaniu LARGEADDRESSAWARE, ale nie wiem jak i gdzie to dodać przy wersji Express…
Czy jest jakaś szansa na wykorzystanie większej ilości ramu niż tylko 2GB?
Sprawdź, czy uda Ci się zwiększyć ilość elementów w tym słowniku, gdy zamienisz typ tego obiektu ze struct na class oraz klucz z long na int lub uint (oczywiście w przypadku aplikacji 64-bitowej). Wykonaj ten drobny test i daj znać o wynikach (możesz też przy okazji pokazać kod tej struktury).
Zamień Dictionary na List z predefiniowaną ilością elementów podawaną w konstruktorze (wówczas na pewno uda Ci się upchnąć więcej elementów). Taka zamiana ma sens, bo dla właściwości Item mamy:
public struct Transpose
{
public int val { get; set; }
public int bound { get; set; }
public int depthTT { get; set; }
}
static int pojemnosc_TT = 60000000;
public static Dictionary transpozycja = new Dictionary(pojemnosc_TT);
Zmiana klucza z long na int raczej nie wchodzi w grę - każdy klucz typu long to pewna liczba generowana według planszy 15x15 elementów gdzie mogą wystąpić 3 stany na każdej pozycji - ilość kombinacji jest olbrzymia, a mi zależy na jak najmniejszej ilości kolizji. Próbowałem zamiany struct na class i otrzymałem dziwne wyniki: 1) w przypadku struct gdy zadeklaruję w kodzie zbyt dużą wartość pojemnosc_TT to aplikacja wcale się nie uruchomi (jak już się uruchomi to działa be błędów) 2) w przypadku class aplikacja uruchamiała się nawet przy pojemnosc_TT=85000000, ale w trakcie obliczeń wywalało błędy z jakimiś adresami pamięci 3) w przypadku struct aplikacja zaraz po uruchomieniu zajmowała w pamięci około 250MB 4) w przypadku class aplikacja zaraz po uruchomieniu zajmowała ponad 2GB 5) zarówno dla struct jak i class w trakcie gry następowało stopniowe zwiększanie zajętości pamięci (dla class dochodziło do około 2.5GB i wywalało błąd) 6) zmieniłem dla class inicjowanie słownika (bez deklarowania pojemności):
public static Dictionary transpozycja = new Dictionary();
…aplikacja na starcie zajmowała około 7MB i dalej rosła zajętość w trakcie gry
niestety class nawet dla 60000000 generowało po pewnym czasie błąd i aplikacja musiała zostać zamknięta - wniosek struct działa o wiele stabilniej
Z uwagi na fakt, że słownik stosuję do zapamiętywania obliczonych w trakcie gry stanów planszy (raz wyliczony stan nie musi być ponownie przeliczany) - wymagam możliwie najszybszego wyszukiwania klucza - lista będzie tu sporo wolnijesza wiec wolę pozostać przy słowniku…
Maksymalna wartość int to 2,147,483,647 (int.MaxValue)
Maksymalna wartość uint to 4,294,967,295 (uint.MaxValue)
Czy ta pewna generowana liczba faktycznie może mieć większą wartość?
Dlaczego wolniejsza? Sprawdzałeś to? Wkleiłem Ci przecież cytat ze strony msdn, że pobranie danego elementu z listy korzystając z indeksu, to operacja o złożoności O(1) (czyli takiej samej jak w przypadku pobrania wartości ze słownika korzystając z klucza).
Z użyciem listy chodzi mi tutaj o to, że jeśli na starcie wiesz, z jakiej liczby obiektów będzie się ona składać, to możesz traktować indeks jak klucz (nawet mógłbyś pomyśleć, czy nie sprawdzi się tutaj zwykła tablica). Lista ta przechowywałaby albo klasy albo struktury, które mogą przyjmować null-e (typ nullable). Jeśli pod danym indeksem będzie null, tzn. że dla tego klucza nie została jeszcze przypisana żadna wartość (taki odpowiednik metody ContainsKey). Jeśli pod danym indeksem nie będzie null-a, tzn. że dla tego klucza została już przypisana jakaś wartość. Jeszcze raz jednak podkreślam. To ma sens wtedy, gdy na wstępie, wiesz z jakiej liczby obiektów będzie się składać ta lista i zainicjalizujesz ją null-ami. W innym wypadku musisz użyć słownika.
BTW jeśli to jest gra, to nie może być tak, że ma ona aż tak gigantyczne zapotrzebowanie na pamięć operacyjną. No chyba, że będzie to gra dekady. Wtedy wszystko da się wybaczyć.
Może zacznę od początku - tworzę sobie grę gomoku (takie kółko i krzyżyk tylko na większej planszy) z możliwością gry z komputerem.
AI komputera wykorzystuje algorytm minimax z cięciami A-B i jako optymalizację dodałem tablicę transpozycji w formie słownika.
Im więcej uda się w trakcie gry zachować raz przeliczonych pozycji tym większe można uzyskać przyspieszenie - w trakcie gry generowanych jest bardzo dużo pozycji powtarzających się i właśnie wtedy słownik bez zbędnego liczenia zwróci mi potrzebną wartość…
Dodatkowo tworzę sobie bazę przeliczonych ruchów - taka baza debiutów połączona z bardzo prymitywnym sposobem uczenia.
Dla zobrazowania przestrzeni stanów na planszy 15x15 można w przybliżeniu podać: 3^225 więc nawet typ long przy tym wymięka…
Nie będę mógł korzystać z pobrania elementu z listy przez indeks, bo musiałbym wcześniej wygenerować całą listę możliwych stanów Lista[(long)klucz].wartosc a jest to niewykonalne…
Tak wielka transpozycja tworzona wyłącznie na czas generowania bazy początkowych ruchów - po zapamiętaniu pewnej ich liczby można zmniejszyć słownik do około 5-8mln.
Okazuje się, że VS ma własny wiersz poleceń i to z niego trzeba wklepać to polecenie…
Niestety przy moich 4GB ramu nie ma różnicy - tak samo mogę odpalić aplikację z około 60mln rekordów w słowniku…
Dodatkowo poleceniem editbin przejechałem wynikowy plik gry - wysłałem koledze co ma 8GB ramu i zobaczę czy u niego teraz uda się odpalić (przygotowana wersja 70mln - wcześniej nie działało mu).