Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
Programmieren<br />
Julia<br />
01 $./julia<br />
Am besten baut man den Pfad zur ausführbaren<br />
Datei nun in seine PATH-Variable<br />
ein.<br />
Wie bei vielen interaktiven Tools kann<br />
man jetzt Ausdrücke eingeben wie:<br />
julia> sqrt(2*7)+(6/4)<br />
5.241657386773941<br />
Wie oben schon erwähnt sind sowohl<br />
eine User Manual wie auch Library-Referenz<br />
online abrufbar <strong>und</strong> helfen bei<br />
der Erk<strong>und</strong>ung von Julia. Weil Julia auf<br />
HPC-Umgebungen ausgerichtet ist <strong>und</strong><br />
Parallelverarbeitung dort eine große Rolle<br />
spielt, ist das auch ein integraler Teil von<br />
Julia.<br />
Eintauchen in Julia<br />
Hochspachen, die die Komplexität von<br />
MPI verbergen, erleichtern den Umgang<br />
<strong>mit</strong> Parallelverarbeitung. Das hat jedoch<br />
seinen Preis in Form einer verminderten<br />
Effizienz. Dieser Preis scheint oft gerechtfertigt,<br />
weil der Bequemlichkeitsvorteil<br />
den Effizienznachteil aufwiegt, aber das<br />
MPI hat nach wie vor seine Berechtigung.<br />
Wie im Julia-Manual beschrieben bietet<br />
Julia eine einfaches, einseitiges Messaging-Modell:<br />
„Julias Implementierung des<br />
Message Passing unterscheidet sich von<br />
der anderer Umgebungen wie etwa MPI.<br />
Die Kommunikation in Julia ist generell<br />
einseitig, was heißt, dass Julia nur <strong>mit</strong><br />
einem Prozessor in einer Zwei-Prozessor-Operation<br />
umgehen muss. Darüber<br />
hinaus handelt es sich bei diesen Operationen<br />
typischerweise nicht um Dinge wie<br />
'Nachricht verschicken' <strong>und</strong> 'Nachricht<br />
empfangen', sondern sie ähneln eher<br />
dem Aufruf von Benutzerfunktionen.“<br />
Die Autoren heben außerdem hervor,<br />
dass Julia zwei eingebaute Pri<strong>mit</strong>ive enthält:<br />
„… Remote References <strong>und</strong> Remote<br />
Listing 1: Julia Header beim Startup<br />
02 _<br />
03 _ _ _(_)_ |<br />
04 (_) | (_) (_) |<br />
Calls. Eine Remote Reference ist dabei<br />
ein Objekt, dass von jedem Prozessor<br />
genutzt werden kann, um auf ein Objekt<br />
zu verweisen, dass ein bestimmter<br />
Prozessor speichert. Eine Remote Call ist<br />
eine Anforderung eines Prozessors, eine<br />
Funktion <strong>mit</strong> bestimmten Argumenten<br />
auf einem anderen (oder demselben) Prozessor<br />
auszuführen.“<br />
Dynamisch erweitern<br />
Für die folgenden Beispiele wird Julia<br />
nun auf einer Multicore-Maschine <strong>mit</strong><br />
zwei Prozessoren gestartet:<br />
julia ‐q ‐p2<br />
05 _ _ _| |_ __ _ | A fresh approach to technical computing<br />
06 | | | | | | |/ _` | |<br />
07 | | |_| | | | (_| | | Version 0.0.0+86921303.rc6cb<br />
08 _/ |\__'_|_|_|\__'_| | Com<strong>mit</strong> c6cbcd11c8 (2012‐05‐25 00:27:29)<br />
09 |__/ |<br />
10 julia><br />
Das dynamische Hinzufügen weiterer<br />
Cores wird im nächsten Abschnitt beschrieben,<br />
im Moment sollen nur zwei<br />
Cores benutzt werden. Es ist außerdem<br />
sinnvoll, die Anzahl Cores nicht zu überzeichnen<br />
(das »‐p«-Argument soll also<br />
nicht die Anzahl Cores in der Maschine<br />
übersteigen).Das Beispiel benutzt einen<br />
»remote_call«, um zwei Zahlen auf einem<br />
andren Prozessor zu addieren. Danach<br />
wird das Resultat abgeholt. Man könnte<br />
genauso wieder einen Remote Call benutzen,<br />
um <strong>mit</strong> dem Ergebnis weiterzurechnen.<br />
julia> r = remote_call(2,+,2,2)<br />
RemoteRef(2,1,1)<br />
julia> fetch(r)<br />
4<br />
julia> s = remote_call(2,+,1,r)<br />
RemoteRef(2,1,2)<br />
julia>fetch(s)<br />
5<br />
Remote Calls kehren un<strong>mit</strong>telbar zurück<br />
<strong>und</strong> warten nicht auf die Beendigung des<br />
Task. Der Prozessor, der den Call absetzte,<br />
fährt <strong>mit</strong> seiner nächsten Operation fort,<br />
während der Call irgendwo anders abgearbeitet<br />
wird. ein »fetch()« wartet allerdings<br />
bis das Resultat<br />
verfügbar ist.<br />
Alternativ kann man<br />
auf die Beendigung<br />
eines Remote Calls<br />
warten, indem man<br />
ein »wait()« <strong>mit</strong> der<br />
Remote Reference<br />
absetzt.<br />
Das eben gezeigte<br />
Beispiel ist durch<br />
den Zwang, die Prozessornummern<br />
explizit anzugeben,<br />
nicht besonders portabel. Die meisten<br />
Programmierer benutzen deshalb ein<br />
Julia-Makro namens »spawn«, das diese<br />
Abhängigkeit eliminiert. Zum Beispiel:<br />
julia> r= @spawn 7‐1<br />
RemoteRef(2,1,7)<br />
julia> fetch(r)<br />
6<br />
Auch das Makro »@parallel« ist in Schleifen<br />
sehr nützlich. Weil Julia interaktiv<br />
ist, muss man sich ins Gedächtnis rufen,<br />
dass auf der Kommandozeile eingegebene<br />
Funktionen nicht automatisch entfernten<br />
Cores zur Verfügung stehen (auf<br />
dem lokalen oder einem entfernten Knoten).<br />
Um Funktionen auf allen Cores zu<br />
verwenden muss man »load()« benutzen.<br />
Diese Funktion lädt Julia-Programme automatisch<br />
auf alle Cores, die <strong>mit</strong> einer<br />
Julia-Instanz assoziiert sind. Alternativ<br />
liest Julia das File »startup.jl« im Homedirectory,<br />
wenn es existiert.<br />
Alle Cores aufrufen<br />
Julia kann Cores der lokalen Maschine<br />
oder von entfernten Maschinen (das sind<br />
immer Cluster-Nodes) nutzen. Wie das<br />
geht, wird im Folgenden demonstriert.<br />
Zuerst startet man die Julia-Instanz <strong>mit</strong><br />
einem Core. Die »nprocs()«-Funktion<br />
zeigt die Anzahl verfügbarer Cores der<br />
aktuellen Instanz an:<br />
$ julia ‐q<br />
julia>nprocs()<br />
1<br />
Möchte man nun weitere lokale Cores<br />
hinzufügen, bietet sich die Funktion<br />
»addprocs_local()« an, wie Listing 2<br />
zeigt. In diesem Beispiel wurde ein Core<br />
hinzugefügt <strong>und</strong> nprocs() zeigt daraufhin<br />
zwei Cores an.<br />
julia>nprocs()<br />
2<br />
Remote Cores (solche von anderen<br />
Maschinen) lassen sich auf zweierlei<br />
Weise hinzufügen. Einmal <strong>mit</strong>hilfe der<br />
»addprocs_ssh«-Funktion. Ein Beispiel<br />
zeigt Listing 3, das je einen Core von<br />
den Knoten n0 <strong>und</strong> n2 einbindet. Die<br />
Voraussetzung dafür ist allerdings, dass<br />
Julia auf allen Nodes an derselben Stelle<br />
installiert oder über ein Shared Filesys-<br />
124 Ausgabe 06-2012 Admin www.admin-magazin.de