Zarządzanie pamięcią pod Windowsem x64

Pracując przy projekcie, który bardzo mocno wykorzystuje obliczenia matematyczne, po raz pierwszy natknąłem się w praktyce na ograniczenia pamięciowe. W pierwszych wersjach systemu nie udało nam się tych ograniczeń zauważyć, gdyż operowaliśmy na małych próbkach danych rzędu 1000x20. Oczywiście należało sprawdzić działanie także dla większej próbki danych i wyskoczyło nam pierwsze z ograniczeń.

Początkowo prace deweloperskie przeprowadzane były na maszynie z systemem 32bitowym z pamięcią 4GB. Występuje tutaj zjawisko ograniczenia pamięci dla procesu do 2GB (ewentualnie do 3GB z odpowiednim przełącznikiem). Przez jakiś czas wystarczał nam obecny limit... Ale po co się ograniczać skoro procesory są 64bitowe? Tak więc po przeinstalowaniu systemu na Windows Server 2008 x64 nastąpiła przedwczesna radość z praktycznie zerowymi limitami (nie mam do dyspozycji serwera z 2TB pamięci RAM ;) ).

Pierwszym problem przy uruchamianiu aplikacji pojawił się z bibliotekami matematycznymi Intel Math Kernel Library, były jeszcze 32bitowe, ale nie sprawiło dużo kłopotów przerobienie jej na wersję 64bitową (aczkolwiek trzeba było trochę doczytać;P ).

W końcu z wielką radością w oczach aplikacja została odpalona i można było patrzeć jak rośnie słupek zajętości pamięci przez proces, urósł do około 4GB. No dobra, ale jakie tutaj są ograniczenia? Okazuje się, że najbliższym limitem jest ciągł obszar jaki można zaalokować. Próba zdefiniowania tablicy typu Byte o liczbie elementów większej niż 2^31 zakończyła się porażką (mniejszą bądź równą - należy wziąć jeszcze narzut platformy .NET - jak najbardziej udało się zaalokować). Dlaczego akurat tablica może mieć rozmiar tylko 2GB? No cóż, wina leży po stronie platformy .NET i środowiska CLR.

Następny krokiem była próba zaalokowania kilku tablic po 2GB każda. Niestety po 6 takich wyświetlił mi się wyjątek OutOfMemoryException (patrz rysunek)

Przyczyną braku pamięci zapewne była wielkość pliku wymiany, gdyż odpowiedni licznik pokazywał ładną liczbę 15999MB (rysunek). Zajętość co prawda wyniosła 14484MB, ale kolejne 2GB tutaj już się nie mieszczą ;)

Ostatecznie muszę stwierdzić, że o ile limit 2GB na wielkość ciągłej pamięci do zaalokowania może boleć (należałoby się zagłębić bardziej w jaki sposób można to obejść), to obszar pamięci dla jednego procesu jest jak na razie nie do zapełnienia dla mnie pożytecznymi danymi, a zawsze można jeszcze zwiększyć rozmiar pliku wymiany.

Warto przeczytać:

  1. BigArray<T>, getting around the 2GB array size limit
  2. Memory Limits for Windows Releases
  3. Zarządzanie pamięcią w 32 i 64-bitowych systemach Windows

Wpis dokonany przez Przemysław Walkowiak dnia 29 August 2008.
Tagi: , , , , | Brak komentarzy

Ns2 i Ns3

Ostatnio przypomniały mi się moje laboratoria z sieci komputerowych i telefonii IP. Polegały głównie na łączeniu komputerów, konfigurowania routingów, translacji adresów, podsłuchiwanie sieci. Czasem dostaliśmy również jakiś ciekawszy sprzęt do zabawy (w stylu router Cisco), ale teraz muszę przyznać, że to były straszne nudy.

Obecnie zostałem poniekąd "zmuszony" do zapoznania się z symulatorami sieci i ocenienia, które z nich spełnią odpowiednie wymagania. Kilka takowych się znalazło (m.in. opnet, omnet++),  ale ich wadami są jak zwykle albo trudnodostępność, albo fakt, że nie są już rozwijane od dłuższego czasu. W momencie, gdy dotarłem do ns2 (w sumie to od niego zacząłem tę przygodę:) ) muszę przyznać, że ktoś odwalił kawał dobrej roboty.

ns2 jest to projekt open-source stworzony przez badaczy dla badaczy. Rdzeń symulatora jest napisany w pełni obiektowo w C++, a skrypty eksperymentów (i nie tylko) można pisać w obiektowej wersji Tcl - OTcl. ns2 umożliwa symulowanie praktycznie każdej warstwy modelu OSI/ISO, począwszy od warstwy fizycznej, poprzez sieciową (wraz z włączeniem różnych protokołów routingu), aż po zachowanie się protokołów warstwy transportowej i aplikacji.

W celu przetestowania własnego protokołu (którejkolwiek z warstw) "wystarczy" go tylko zaimplementować w symulatorze. Ostatecznie nie jest to dosyć trudne, gdyż można wzorować się na już napisanych algorytmów.

Efektem działania symulatora jest plik tekstowy w którym zawarte są wszystkie niezbędne informacje na temat każdego z pakietów jaki w naszej wirtualnej sieci podróżował. Wystarczy teraz zaprząc jakiś parser (np. awk), coś do rysowania wykresów (np. gnuplot) i już możemy się cieszyć naszymi wynikami :)

Dodatkowo jeżeli chcielibyśmy wizualnie zobaczyć co się w naszej sieci dzieje można skorzystać z programu NAM (Network Animator) towarzyszącego ns2. Który na podstawie odpowiedniego pliku wynikowego z ns2 stworzy ładną animacje ruchu na poszczególnych połączeniach węzłów.

To tyle jeżeli chodzi o ns2. Chciałbym jeszcze wspomnieć o kolejnej wersji symulatora ns3, która jest w trakcie powstawiania. W przeciwieństwie do ns2, ns3 jest w całości napisana w C++ (ns2 korzysta również z OTcl) i jak na razie skrypty eksperymentów to są programy napisane w C++ (aktualnie powstają bindingi do pythona). Ciekawostką jeżeli chodzi o ns3 jest to, że wspiera on format plików pcap, dzięki czemu możemy bezproblemowo odczytać wyniki naszej symulacji albo poprzez tcpdump, albo wireshark <-- czyż to nie jest dużo wygodniejsze niż pliki tekstowe? :P

Więcej informacji można znaleźć na stronie symulatora:

  1. ns2 i nam
  2. ns3

Wpis dokonany przez Przemysław Walkowiak dnia 20 August 2008.
Tagi: , , | 3 komentarzy

Matematyka i C#

Wiele razy już w przeciągu ostatnich 2-3 lat szukałem jakiejś porządnej biblioteki implementującej podstawowe algorytmy z algebry liniowej i ogólnie z matematyki. Wbrew pozorom znalezienie tychże dla platformy .NET nie jest takie trywialne..

Po odpytaniu google o jakikolwiek silnik matematyczny natrafiamy na cenioną open sourcową bibliotekę GSL. Z mojego punktu widzenia ma ona jedną wadę: jest mocno nastawianona na środowisko Linuksowe. O ile istnieją wersje przeportowane na system Win32 (nie mówię tutaj o Cygwinie) to nie były już aktualizowane kilka lat. A o automagicznym podpięciu się z poziomu C# można praktyczznie pomarzyć..

Następna kolekcja matematyczna jak wpadła mi w ręce to dnAnalytics. Muszę przyznać, że jest ona bardzo dobrze przygotowana zapewniając bardzo wygodny i intyuicyjny intefejs programistyczny. Z podstawowych możliwości udostępnianych mogę wymienić własną implementację struktury do obsługi macierzy i wektorów, możliwość wczytywania i zapisywania danych z/do plików CSV, chyba wszystkie najważniejsze operacje na macierzach oraz szereg algorytmów do rozwiązywania równań, dekompozycji macierzy (SVD, LU, QR) i co najważniejsze ma zaimplementowane wsparcie dla natywnej biblioteki Intel Math Kernel Library, dzięki czemu znacząco zyskujemy na wydajności. Niestety dnAnalytics miał jedną wadę: nie w pełni wykorzystywał możliwości MKL.

W ten sposób dotarłem (całkiem przypadkowo) od ILNumerics.Net. Porównując z dnAnalytics to ma raczej trochę inny interfejs (ani nie lepszy, ani nie gorszy, po prostu inny) przypominający bardziej język skryptowy  z Scilaba czy Matlaba (między innymi odwoływanie się do pól macierzy). A skoro jestem przy macierzach to warto wspomnieć, że w tym przypadku nie jesteśmy ograniczeni tylko do dwóch wymiarów i typu double, ale dzięki typom generycznym możemy wykorzystać niemal dowolny typ numeryczny w wielomiarowej przestrzeni. Malutkim minusem jest brak obsługi plików CSV do których przyzwyczaiła mnie dnAnalytics, ale coś za coś. Na zakończenie jeszcze jeden ogromny plus: w pełni wykorzystuje zestaw funkcji LINPACK z natywnej biblioteki MKL, a także dodatkowo z konkurencyjnej biblioteki firmy AMD. Dla zachowania kompatybilności z platformą mono powstała również specjalna wersja natywnego silnika matematycznego.

Podsumowując dnAnalytics jest bardzo przejrzystą biblioteką nadającą się do projektów w których nie wymaga się dużej wydajności i ograniczamy się do dwóch wymiarów. ILNumerics.Net jest natomiast prawdziwą perełką wśród otwartych bibliotek matematycznych, a dzięki porządnemu połączeniu z MKL i pochodnymi jest naprawdę wydajnym tworem.

Wpis dokonany przez Przemysław Walkowiak dnia 29 July 2008.
Tagi: , , , | Brak komentarzy

Visual Studio 2008 SP1 Beta i .NET 3.5 SP1 Beta

Parę dni temu coś mnie podkusiło do instalacji wersji beta nowego service packa do VS2008 i .NET3.5. Instalacja przeszła względnie bezboleśnie (musiałem tylko zwolnić trochę miejsca na dysku;) ).

I wszystko byłoby OK, gdyby nie fakt, że RubiksCube v2 przestał działać..
A dokładniej po wykonaniu dowolnego ruchu wywalał się bliżej nieokreślony błąd w PresentationCore.dll (bliżej nieokreślony, bo opisu błędu wogóle nie było). Co lepsze przed SP1 Beta aplikacja działała prawidłowo...

Błędu szukałem komentując po kolei poszczególne fragmenty kodu i dotarłem do takiej konstrukcji

  1. if (model == null)
  2. {
  3. model = new Model3DGroup();
  4. }
  5. else
  6. {
  7. model.Children.Clear();
  8. }

Gdzie model jest typu Model3DGroup. Doszedłem do tego, że aplikacja wywalała się przez linijkę 7. Nie wiem dlaczego tak się zachowywał, ale błąd naprawiłem w następujący sposób:

  1. if (model == null)
  2. {
  3. model = new Model3DGroup();
  4. }
  5. else
  6. {
  7. model.Children = new Model3DCollection();
  8. }

I jak na razie aplikacja śmiga :P

Wpis dokonany przez Przemysław Walkowiak dnia 24 May 2008.
Tagi: , , , , , | Brak komentarzy

Kostka, kosteczka i kostkunia

Dokonałem ostatnio dosyć spontanicznego zakupu dwóch kostek Rubika. Nie byłoby w tym nic dziwnego gdyby nie to, że nie są to już klasyczne kostki 3x3x3, ale 4x4x4 oraz 5x5x5 :) . Nie był to może mały wydatek, ale jestem bardzo z niego zadowolony. Wraz z swoją starszą siostrą można je podziwiać na poniższych zdjęciach :P

050208-1541-kostkakoste1.jpg
050208-1541-kostkakoste2.jpg

Jeżeli chodzi o zdolność układania to 4x4x4 udało mi się po raz pierwszy ułożyć po około 2h. Następne próby trwały już około 1h. W następnej kolejności zabrałem się za piątkę, ostatecznie również skończyłem po około godzinie :). Od teraz chyba nie będę się nudził już na żadnym z wykładów :D

Wpis dokonany przez Przemysław Walkowiak dnia 2 May 2008.
Tagi: , | 2 komentarzy

Debugger w Visual Studio

Jak wiadomo debugger dla programisty to jest bardzo ważne narzędzie, które potrafi znacząco ułatwić pracę. Moim zdaniem w Visual Studio znajduje się chyba jeden z najlepszych pod względem intuicyjnego używania (aczkolwiek dawno nie korzystałem z produktów Borlanda, tam może też się co nieco zmieniło;) ). Nie będę tutaj opisywał jak z poziomu środowiska IDE obsługiwać to narzędzie, gdyż to raczej każdy już sie z tym zapoznał, ale jak z niego korzystać z poziomu kodu.

W ogólności służy do tego klasaSystem.Diagnostics.Debugger. Co prawda posiada ona tylko trzy metody przydatne dla programisty, ale liczba ich nie jest istotna;).

  • Debugger.Break() - powoduje zatrzymanie działania programu i włączenie debuggera (coś a la standardowy breakpoint)
  • Debugger.Launch() - uruchamia debugger i podłącza go do aktualnego procesu, ale jeszcze go nie zatrzymuje
  • Debugger.Log(int level, string category, string message - wykorzystuje w przypadku, gdy chcemy wysłać na konsolę debuggera informację zwrotną. Nie potrzebujemy w tym wypadku tworzyć aplikacji konsolowej - w ogóle nie musi ona posiadać jakiegokolwiek okna ;)

Ciekawostką jest to, że wywołania tych metod zostają usuwane w wersji Release projektu. Są one dostępne jedynie w wersji Debug.
Przydatności metody Log() chyba nie trzeba tłumaczyć. Zawsze przecież przydają się dodatkowe informacje w przypadku, gdy badamy rozbudowaną i przebiegającą przez wiele iteracji funkcję.

Break() możemy oczywiście zastąpić standardowym Breakpointem z IDE, ale ma taką zaletę, że zadziała nawet wtedy gdy nie będziemy uruchamiali aplikacji z VisualStudio. Przydatne przy badaniu WebService'ów zainstalowanych na IIS;)

Sąsiednią klasą towarzyszącą System.Diagnostics.Debugger jest System.Diagnostics.Debug udostęniająca asercje (Assert(bool condition) - przy czym warunek musi być spełniony, aby program działał poprawnie) oraz bardziej rozbudowane metody do obsługi wyjścia w konsoli debuggera takie jak Write, WriteIf, WriteLine, WriteLineIf, Indent, Unindent, Print. W każdym bądź razie polecam się z nimi zapoznać :)

Wpis dokonany przez Przemysław Walkowiak dnia 2 March 2008.
Tagi: , , , | Brak komentarzy

Wykorzystanie wyrażeń lambda w naszych klasach

Wyrażenia lambda jest to nowa konstrukcja wprowadzona do C# 3.0 pozwalająca w łatwy i przejrzysty sposób na "szybko" zdefiniować jakąś funkcję. W tej notce napiszę jak można je użyć do definiowan

Nadrzędnym typem każdego wyrażenia lambda jest typ Func<T1, T2, T3, T4, TResult> gdzie T1-T4 są typami argumentów, a TResult typem wyniku. Nie jesteśmy oczywiście przywiązani do czterech parametrów wejściowych, gdyż zostały zdefiniowane typy od 0 do 4 argumentowe (zmienia się liczba wystąpień TX). TResult jest natomiast parametrem obowiązkowym.

W poniższym przykładzie zdefiniuję metodę w klasie Matrix, która jako argument będzie otrzymywała wyrażenie lambda (funkcję) i wykona ją dla każdego z elementów macierzy. Przechodząc do praktyki będzie to wyglądało tak:

  1. public void DoForAllElements(Func< T , T> function)
  2. {
  3.  
  4. for (int i = 0; i < N; i++)
  5. {
  6. for (int j = 0; j < M; j++)
  7. {
  8. this[i, j] = function(this[i, j]);
  9. }
  10. }
  11. }
  12.  

Wykorzystać ją możemy w sposób następujący:

  1. var A = new Matrix< Double , DoubleCalculator>();
  2. A.DoForAllElements( x => Math.Sqrt(x) );
  3.  

Prawda, że proste? :)

Wpis dokonany przez Przemysław Walkowiak dnia 24 February 2008.
Tagi: , | Brak komentarzy

Typy generyczne w C# i „drobne” niedociągnięcie

Zacznę może od tego, że potrzebuję w pewnym projekcie pisanym w C# obsługę macierzy (dodawanie, mnożenie, ślad, itp). Konkretniej chodzi o macierz skonstruowaną z elementów typu prostego Double, ale dobrą cechą byłaby jej uniwersalność (czyli obsługa dowolnych typów) – w skrócie macierz generyczna. Ponieważ jestem troszku leniwy więc rozpocząłem poszukiwania odpowiednich bibliotek w Internecie. Niestety jedynym rozwiązaniem godnym przejrzenia, aczkolwiek niespełniącym moich wymagań była biblioteka CSML. Wadą jej polega na tym, że natywnie operuje na liczbach zespolonych (co można oczywiście zarzutować w przestrzeń liczb rzeczywistch) i nie ma domyślnie zrobionej konwersji Double na Complex.

Zdziwiło mnie, że taka ważna struktura w obliczeniach numerycznych nie dorobiła się pożądnej implementacji (dodam jeszcze: darmowej).. No cóż, w takim razie należy samemu napisać odpowiedni kod.

Zacząłem od stworzenia szkieletu klasy:

  1. public class Matrix< T >
  2. {
  3. T[,] matrix;
  4. public int N { get; private set; }
  5. public int M { get; private set; }
  6. public Matrix(int n, int m)
  7. {
  8. }
  9.  
  10. public T this[int i, int j]
  11. {
  12. }
  13. public static Matrix< T > operator +(Matrix< T > A, Matrix< T > B)
  14. {
  15. }
  16. public static Matrix< T > operator -(Matrix< T > A, Matrix< T > B)
  17. {
  18. }
  19. public static Matrix< T > operator *(Matrix< T > A, Matrix< T > B)
  20. {
  21. }
  22. }
  23.  

 

Dokonałem następnie implementacji pierwszego z operatorów:

  1. public static Matrix< T > operator +(Matrix< T > A, Matrix< T > B)
  2. {
  3. if (A.N == B.N && A.M == B.M)
  4. {
  5. Matrix< T > result = new Matrix< T >(A.N, A.M);
  6. for (int i = 0; i < A.N; i++)
  7. {
  8. for (int j = 0; j < A.M; j++)
  9. {
  10. result[i, j] = A[i, j] + B[i, j];
  11. }
  12. }
  13. return result;
  14. }
  15. else
  16. {
  17. throw new Exception("Matrices have different dimensions");
  18. }
  19. }

Próba kompilacji i... błąd: "Operator '+' cannot be applied to operands of type 'T' and 'T'". W sumie prawda, skąd kompilator ma wiedzieć, czy typ T ma zaimplementowany operator dodawania. Właśnie po to wprowadzono system reguł mogący wymusić na danym typie kilka cech. Zaglądam więc do dokumentacji MSDN i niestety nic ciekawego nie znalazłem. Odpalam wyszukiwarkę dotarłem do kilku wątków o tym samym problemie i jedyną odpowiedzią jest to, że: nie można czegoś takiego zrobić ponieważ Double jest typem prostym.

Ostatecznie jest jeszcze jedno rozwiązanie ("Using generics for calculations"), ale moim zdaniem nie do końca jest ono wzorowe z tego względu, że należałoby stosować dodatkową warstwę pomiędzy typami Int32, Double a naszą klasą generyczną. Ale dużego wyboru nie ma..

Wpis dokonany przez Przemysław Walkowiak dnia 23 February 2008.
Tagi: , , | 1 komentarz

Microsoft SQL Server 2008 CTP6 (February 2008)

Dwa dni temu na stronach Microsoftu można było zauważyć długo oczekiwane następne wydanie CTP SQL Servera 2008, które w przeciwieństwie do CTP5 zaczęło oficjalnie wspierać LINQ to SQL w Visual Studio 2008. Pierwszy kontakt z CTP6 wyszedł dosyć dobrze, bez problemów dało zainstalować się na kilku serwerach (opartych na Windows Server 2008) – nie pojawił się żaden błąd i wszystko działało od ręki. Problemy zaczęły się dopiero w momencie, gdy zapragnąłem zainstalować go na moim laptopie. A zaczęło się to mniej więcej tak ;)

Odinstalowałem (tzn. spróbowałem odinstalować) wpierw poprzednią wersję SQL Server 2008 oraz SQL Server Express 2005, niestety ten proces nie do końca się powiódł, gdyż wystąpił błąd o niezbyt rozbudowanej treści "Setup has encountered the following error: Failed to retrieve data for this request..". W Internecie jedyne co udało mi się znaleźć to opis, że trzeba ręcznie usunąć aplikacje (np. za pomocą Windows Install Clean Up, a następnie wyczyścić pliki z katalogu Program Files). Tak więc też uczyniłem.

Po restarcie systemu i odpaleniu instalatora SQL Server 2008 w niecałe 30s pojawił się znowu identyczny błąd (gdy odpaliłem ręcznie instalator SQL Database Engine wyświetlił jakieś dziwne zapytanie SQL i również instalacja się nie powiodła). Google już niestety na ten temat milczało. Ale ponieważ tak łatwo nie można się poddać, więc szukałem dalej;).

Rozwiązanie problemu znajdowało się w sercu systemu Windows – w rejestrze :) Postanowiłem być trochę brutalny i usunąłem wszystkie wpisy związane z SQL Serverem z klucza HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ (delikatne próby usuwania wybiórczych wpisów także spełzły na niczym). Efektem tego było szczęśliwe zainstalowanie Microsoft SQL Server 2008 CTP6. Można więc wracać już do zabawy :)

Download: Microsoft SQL Server 2008 CTP6 (February 2008)

Wpis dokonany przez Przemysław Walkowiak dnia 22 February 2008.
Tagi: , , | Brak komentarzy

LINQ to XSD

Dzisiaj wyszła kolejna wersja Alpha 0.2 LINQ to XSD. Tym razem już działająca z Visual Studio 2008. Niestety chwilowo nie mam czasu, aby dokończyć moją kostkę, ale wiem już przynajmniej jakiego rozwiązania wybiorę :)

Download: LINQ to XSD Alpha 0.2

Wpis dokonany przez Przemysław Walkowiak dnia 21 February 2008.
Tagi: , , , | Brak komentarzy

« Poprzednie wpisy