Unabhängige Zufallszahlen / Random Klasse im .NET Framework

Heute hat mir ein Kollege erzählt, dass die Random Klasse im .NET Framework keine vernünftigen Zufallszahlen erzeugen würde und dass zwei Instanzen der Random Klasse jeweils die gleichen Zufallszahlen erzeugen. Das entsprechende Beispiel hat er mir schnell anhand eines kleinen NUnit-Tests gezeigt, der in etwa so aussah:

Lässt man das Programm laufen und vergleicht die beiden erzeugten Listen stellt man fest, dass beide Listen die gleichen Zufallszahlen enthalten, was meinen Kollegen etwas überrascht hat. Der Grund ist dafür aber eigentlich recht einfach und auch in der Hilfe zum .NET Framework dokumentiert. Die Initialisierung des Pseudozufallszahlengenerator wird anhand der Systemzeit durchgeführt und da die Random Klasse nur einen recht einfachen Zufallszahlengenerator beinhaltet, liefern beide Klassen jetzt die gleichen Werte zurück.
Das Problem kann man auf unterschiedlichen Wegen lösen. Zum Einen kann man einfach nur ein Objekt verwenden, das die Pseudozufallszahlen zurückliefert. Benötigt man trotzdem zwei unterschiedliche Instanzen der Klasse Random könnte man mittels System.Environment.TickCount die Klassen unterschiedlich instantiieren, bspw. so:

Eine letzte Möglichkeit ist es, einen Zufallszahlengenerator zu verwenden, der kryptographisch sichere Zufallszahlen erzeugt. So einen Zufallszahlengenerator bietet das .NET Framework unter System.Security.Cryptography.RandomNumberGenerator. Das Programm wird dadurch allerdings etwas aufwendiger, da ein solcher Zufallszahlengenerator immer in ein Byte-Array schreibt und man das Array dann erst in den benötigten Variablentyp umwandeln muss. Das Programm von oben würde unter Verwendung eines solchen Zufallszahlengenerator dann so aussehen:

Klingt doch eigentlich logisch, oder? Jedenfalls sollte man bei der Verwendung der Klasse Random etwas aufpassen und daran denken, dass bei Initialisierung mit gleichen Werten auch die gleichen Zufallszahlen zurückgegeben werden und sogar vorhersagbare Zufallszahlen zurückgeliefert werden (was in den meisten Fällen aber kein Problem sein sollte).

Inhalt eines Ordners mit einer ZIP Datei und MD5 prüfen

In den letzten Tagen habe ich mir die Frage gestellt, wie ich erkennen kann, ob die Dateien, die ich auf einem Server abgelegt habe, wirklich noch ihren richtigen Inhalt haben oder ob sie evtl. durch Übertragungsfehler o.ä. geändert wurden. Die übliche Methode ist, dass man bspw. den Inhalt einer ZIP Datei mit einem MD5 Hash abgleicht, aber das hilft bei einem Ordner dann auch nicht weiter. Aus diesem Grund habe ich mich mal daran gemacht und ein kleines Programm geschrieben, das zuerst die ZIP Datei mit einem MD5 Hash vergleicht und anschließend dann den Inhalt des ZIP Datei mit dem Inhalt des Ordners vergleicht und neben einer GUI auch per Kommandozeile gesteuert werden kann, damit man die Prüfung in einer Batch-Datei durchführen kann.

imageimageimage

Den Sourcecode und mehr Informationen gibt es auf GitHub. Eine kompilierte erste Version habe ich hier abgelegt: MD5ZipFolderCheck

Matrix Multiplikation auf der Grafikkarte mit .NET und Cudafy

Die Parallelisierung von Algorithmen und Programmteilen hat mir schon immer Spaß gemacht und ich habe mir sogar zu Hause einen Rechencluster gebaut, mit dem ich experimentieren konnte. In der aktuellen dotnetpro Ausgabe (07/2013) beschreibt Bernd Marquardt, bei dem ich auf der Parallel 2012 Konferenz einen .NET TPL Workshop mitmachen durfte, in einem Artikel die Parallelisierung von Algorithmen mit AMP unter C++, die dann auf der Grafikkarte ausgeführt werden. Leider muss man hier immer noch den Umweg über C++ gehen, aber glücklicherweise gibt es für .NET mit Cudafy ein Framework, mit dem man diesen Umweg nicht gehen muss. Cudafy unterstützt, neben dem namensgebenden CUDA von Nvidia, auch OpenCL, sodass man damit plattformübergreifende Parallelisierungen vornehmen kann. Ich habe in den letzten Tage ein wenig mit Cudafy herumgespielt und möchte hier einmal ein recht simples Beispiel der Matrixmultiplikation beschreiben.

Um Cudafy zu verwenden, benötigen wir zuerst ein GPGPU Objekt, das unsere zu verwendende Hardware repräsentiert. Cudafy unterstützt dabei sowohl CUDA von Nvidia als auch OpenCL der Khronos Group. Ich habe mich in meinem Beispiel für OpenCL entschieden, denn so konnte ich sowohl die CPU als auch die beiden Grafikkarten in meinem System (Intel und Nvidia) zur Berechnung auswählen. Folgender Codeschnippsel zeigt die Instanziierung des GPGPU Objektes, ein ganz rudimentäres Exception Handling (jaja, ich weiss ;-)) und die Instanziierung meiner Matrixmultiplikationsklasse.

 

Wichtigster Grundstein meiner Entwicklungsumgebung ist eine abstrakte Basisklasse, von denen meine implementierten Algorithmenklassen erben. Dieser Schritt ist nicht unbedingt notwendig, macht aber in meinem Fall Sinn, da ich beim „Herumspielen“ nicht nur die Matrixmultiplikation implementiert habe, sondern auch andere Algorithmen.

Im Konstruktor muss ein GPGPU Objekt übergeben werden, das die ausgewählte Client Hardware repräsentiert (die Instanziierung wurde bereits weiter oben beschrieben). Die Execute Methode enthält nun den Code, der benötigt wird, m einen Kernel mit Cudafy für die zu verwendende Client Hardware vorzubereiten (Aufruf von Cudafy(…) und LoadModule(..)). Außerdem wird noch eine Zeitmessung mit der allseits bekannten Stopwatch() durchgeführt. In diesem Block wird die innerhalb der Algorithmenklasse zu implementierenden Methode OnExecute() aufgerufen, sodass nach Ausführung die Dauer der Berechnung (inklusive der Übertragungszeit der Daten zur berechnenden Hardware) in der Konsole ausgegeben wird.

Die Klasse zur Matrixmultiplikation sieht dann so aus.

Wie bereits erwähnt, ist diese Klasse während meiner Tests entstanden, sodass ich in diesem Beispiel Testdaten verwenden, die ich bei Aufruf des Konstruktors generiere. Dem Konstruktor wird in diesem Beispiel neben dem GPGPU Objekt auch noch die Dimension der Matrix mit übergeben. Die wichtigsten Teile des Codes stecken aber in der Methode OnExecute() und Multiply(…). Die Methode OnExecute() ist dafür verantwortlich, den Speicher auf der Client Hardware anzulegen und die Arrays vom Host auf den Client zu übertragen. Dies wird hier durch die Methode CopyToDevice(…) vorgenommen. Das Ergebnisarray wird nicht auf die Client Hardware kopiert, sondern nur der Speicher reserviert, da es zu Beginn sowieso leer ist und deshalb keine Daten benötigt werden. Die Launch(…) Methode startet dann die Berechnung auf der Client Hardware. Den Aufruf werde ich hier nicht weiter erläutern, mehr zur Launch(…) Methode findet man aber in der Cudafy Dokumentation. Nach dem Aufruf wird das Ergebnis von der Hardware wieder auf den Host übertragen und der belegte Speicher auf dem Client wieder freigegeben. Das war’s auch schon.

Die Client Implementierung des Algorithmus steckt in der mit dem „Cudafy“ Attribut markierten Methode Multiply(…). Die Methodenparameter sind GThread (wird standardmäßig von Cudafy hinzugefügt und enthält Statusinformationen), die Dimension der Matrix und die Matrizen für die Matrixmultiplikation. In dieser Methode wird zuerst die x und y Position innerhalb der Matrix errechnet. Die Berechnung orientiert sich hier an den Blöcken, die über die Launch Methode mit übergeben werden. Genauere Informationen dazu findet ihr in den Grundlagen zum Thema OpenCL und CUDA. Im nächsten Schritt wird vorsichtshalber noch überprüft, dass x und y auch wirklich innerhalb der Matrix liegen und anschließend die Matrixmultiplikation durchgeführt, die dann im Ergebnisarray gespeichert wird.

Die Methode CpuCalculation() habe ich implementiert, um die Geschwindigkeit einer einzelnen CPU mit der Ausführung auf einem Client zu vergleichen. Auf meinem Rechner war eine Nvidia GT650M bei einer Dimension von 256 bereits 3x so schnell wie ein Kern meiner i7 CPU. Bei einer Dimension von 512 war sie schon um den Faktor 7,5 schneller.

Abschließend kann man sagen, dass sich die Nutzung einer Grafikkarte als Client bei einem hinreichend großen Problem lohnt und man damit eine große Beschleunigung erreichen kann. Dank Cudafy funktioniert das alles auch in .NET ohne den Umweg über C++. In meinem Beispiel bleibt außerdem noch viel Platz für Optimierungen, da ich das Beispiel möglichst simpel halten wollte – und das gilt sowohl für die Berechnung der Matrixmultiplikation auf der CPU (hier wäre bspw. eine Implementierung mit der TPL sinnvoll, die dann alle Kerne nutzt) als auch auf der GPU.

BufferedStream oder nicht?

Ab und an trifft man immer wieder auf elementare Fragen beim Programmieren, die man sich vorher in der Art noch nicht gestellt hat. Diesmal war es generell der FileStream in .NET. Die Frage ist halt, ob die FileStream Klasse in .NET schon einen Buffer verwendet oder ungebuffert ist, sodass man sie besser in einen BufferedStream einpackt, um die Performance zu erhöhen. Glücklicherweise bin ich auf einen Blogeintrag von Brad Abrams im MSDN gestossen, der diese Frage eindeutig beantwortet:

Bei einem FileStream kann im Konstruktor eine Buffergröße mitgegeben werden, d.h. dass ein FileStream generell gebuffert wird und man ihn somit nicht mehr extra in ein BufferedStream verpacken muss.

Tja, da man nicht reinschauen kann, war das nicht von vornherein klar, aber da das momentan für mich wichtig ist (ich brauche eine performante Schreibmethode für Daten), ist es nicht schlecht, wenn man das weiss und nicht unnötig Balast aufbaut.