Verwendung von push (zur Parallelisierung) aus Modul heraus

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
BigFiszh
User
Beiträge: 6
Registriert: Samstag 28. Februar 2015, 22:00

Hallo Freunde, ich habe alle "Warnungen" an Neulinge gelesen, nun wage ich mich mal aus der Deckung. Und werde mein Problem so klar wie möglich beschreiben. Das Einzige, was mir jetzt noch passieren kann, ist dass ich einen total simplen Denkfehler habe - und dann trotzdem gesteinigt werde. Aber ich versuch´s mal. :)

Also folgendes Problem: ich möchte einen Funktionsaufruf auf mehreren Prozessoren parallelisieren. Die Funktion hat mehrere (!) Inputparameter, einer davon soll über map_sync kommen, die anderen sollen statisch sein. Da map_sync alle Parameter iteriert, habe ich versucht, die statischen Parameter über "push" zu übergeben.

Das nachstehende Beispiel - natürlich stark vereinfacht - illustriert, was ich vorhabe:

Code: Alles auswählen

In [26]:

from IPython.parallel import Client

def mul(b):
    return a*b

def mmul(a,b):
    lvClient = Client()
    v = lvClient[:]
    v.push(dict(a=a))
    return v.map_sync(mmul, range(b))

In [27]: mmul(2,5)

Out[27]: [0, 2, 4, 6, 8]
Soweit so gut, wenn ich das direkt im iPython Notebook laufen lasse (wie hier gezeigt), funktioniert es prächtig. Wenn ich den gesamten Code hingegen (unverändert) in eine Datei auslagere ('mymultitest.py'), dann klappt es nicht mehr:

Code: Alles auswählen

In [24]: import mymultitest

Out[24]: <module 'mymultitest' from 'mymultitest.pyc'>

In [25]: mt = mymultitest.mmul(2,5)

[0:apply]: 
-------------------------------------------------------------------------
NameError     Traceback (most recent call last)<string> in <module>()
E:\iPython\mymultitest.pyc in mmul(b)
      4 
      5 def mul(b):
----> 6     return a*b
      7 
      8 def mmul(a,b):
NameError: global name 'a' is not defined
Was mache ich falsch? Habe ich einen Denkfehler?
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@BigFiszh: a ist in der Funktion mul eine globale Variable. Da hilft auch kein pushen und pullen, wobei Du gar nichts pullst. Das ist nämlich nur eine simple Pipe. Das a hast Du in den 25 nicht gezeigten Eingaben irgendwann einmal gesetzt. Deshalb ist es im interaktiven Modus da, als Modul aber nicht.
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

BigFiszh hat geschrieben:Soweit so gut, wenn ich das direkt im iPython Notebook laufen lasse (wie hier gezeigt), funktioniert es prächtig. Wenn ich den gesamten Code hingegen (unverändert) in eine Datei auslagere ('mymultitest.py'), dann klappt es nicht mehr:
In IPython bist Du schon bei Eingabezelle 26. Alles was Du davor gemacht hast, hat auch Einfluss auf die aktuelle Zellenberechnung. Um diese Effekte auszuschalten, kannst Du in IPython Notebook den Kernel neustarten, danach solltest Du in IPython Notebook die gleiche Fehlermeldung bekommen und kannst Dir überlegen wie Du a korrekt übergibst.
a fool with a tool is still a fool, www.magben.de, YouTube
BigFiszh
User
Beiträge: 6
Registriert: Samstag 28. Februar 2015, 22:00

Hier habe ich den Kernel komplett neugestartet, ein leeres Notebook erzeugt und den Code direkt eingegeben - und zur Sicherheit habe ich sogar den Namen "a" in der Hauptfunktion in x geändert, um auszuschließen, dass der Variablenname irgendwie "hängen" bleibt, funktioniert trotzdem reibungslos:

Bild

Das hätte ich eigentlich auch erwartet, um ehrlich zu sein? Soweit ich es gelesen habe, stellt "push" die Variable in den Namensräumen aller verwalteten Prozesse zur Verfügung. Der "pull" ist im einzelnen Prozess selbst gar nicht notwendig, weil die Variable eben zur Verfügung steht. Mit "pull" kann ich Variablen wieder aus dem Subprozess in den Hauptprozess zurückholen (also von da, wo der Push kam).

Noch weitere Ideen, warum es so klappt, aber im Modul nicht?
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Mein schlaues Buch "IPython Interactive Computing and Visualization Cookbook" sagt dazu im Rezept
"Distributing Python code across multiple cores with IPython":
First we launch four IPython engines in separate processes. We have basically two options to do this:
  • Executing "ipcluster start -n 4" in a system shell
  • Using the web interface ... clicking on the Cluster tab and launching four engines
a fool with a tool is still a fool, www.magben.de, YouTube
BlackJack

@BigFiszh: In *welchem* Namensraum stellt das `push()` den Namen denn zur Verfügung? Dir ist klar das Module einen eigenen Namensraum darstellen?
BigFiszh
User
Beiträge: 6
Registriert: Samstag 28. Februar 2015, 22:00

MagBen hat geschrieben:Mein schlaues Buch "IPython Interactive Computing and Visualization Cookbook" sagt dazu im Rezept
"Distributing Python code across multiple cores with IPython":
First we launch four IPython engines in separate processes. We have basically two options to do this:
  • Executing "ipcluster start -n 4" in a system shell
  • Using the web interface ... clicking on the Cluster tab and launching four engines
Schon klar, was sagt das? Die Prozesse sind gestartet, sonst würd´s gar nicht laufen, sondern schon bei "Client()" eine Exception geben ... aber das sagt ja nix über das eigentliche Problem aus, oder? Und die Engines laufen ja, die Fehlermeldung kommt acht Mal - je einmal für jeden Kern. :) Ich hatte den Screenshot nur auf die erste Ausgabe abgeschnitten ...
BlackJack hat geschrieben:@BigFiszh: In *welchem* Namensraum stellt das `push()` den Namen denn zur Verfügung? Dir ist klar das Module einen eigenen Namensraum darstellen?
Ja, das ist mir klar. Aber ich ging davon aus, dass jeder Prozess in dem Cluster ebenfalls einen eigenen Namensraum hat. Und dass die push-Funktion alle übergebenen Variablen in ebendiesen Namensräumen verfügbar macht. Wenn ich im Notebook beispielsweise auf die push-Funktion verzichte, sondern stattdessen einfach sage = "a = x", dann beschwert sich die Funktion auch im Notebook - was doch bedeutet, dass die aufgerufene Funktion eh in einem separaten Namensraum läuft:

Bild

Und dann ist es doch egal, ob der aufrufende Namensraum der des Notebooks - oder der eines Moduls ist, oder? Ungefähr so hat sich "Klein Fritzchen" diese Namensraumwelt vorgestellt. :D

Bild

Damit wäre die gepushte Variable dann gewissermaßen als globale Variable im Namensraum jeder Engine verfügbar:

v.push?

Code: Alles auswählen

Type:        instancemethod
String form: <bound method DirectView.push of <DirectView [0, 1, 2, 3,...]>>
File:        c:\users\jens\anaconda\lib\site-packages\ipython\parallel\client\view.py
Definition:  v.push(self, ns, targets=None, block=None, track=None)
Docstring:
update remote namespace with dict `ns`
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

BigFiszh hat geschrieben:Schon klar, was sagt das? Die Prozesse sind gestartet, sonst würd´s gar nicht laufen, sondern schon bei "Client()" eine Exception geben ... aber das sagt ja nix über das eigentliche Problem aus, oder? Und die Engines laufen ja, die Fehlermeldung kommt acht Mal - je einmal für jeden Kern. :)
Hast Du das Script mit ipcluster start -n 8 auf der Konsole gestartet?
a fool with a tool is still a fool, www.magben.de, YouTube
BlackJack

@BigFiszh: Das `a` ist dann "im Namensraum" jedes Prozesses verfügbar aber eben dort nicht in jedem Modul. Genau so wenig wie in der IPython-Shell ein ``a = 42`` dazu führt das `a` nun plötzlich in jedem importiertem Modul in dieser Shell existiert. Der Namensraum der Shell und der von importieren Modulen sind getrennt. Und so ist das bei Remote-Prozessen in IPython auch.
BigFiszh
User
Beiträge: 6
Registriert: Samstag 28. Februar 2015, 22:00

OK, das ergibt Sinn, vielen Dank! Dann werde ich mir mal Gedanken machen, wie ich das anders lösen kann ... beispielsweise auf diese Art:

Code: Alles auswählen

%pylab
from IPython.parallel import Client

def mul(a,b):
    return a*b

def mmul(x, y):
    lvClient = Client()
    v = lvClient[:]
    xa = array(x).repeat(y)
    ya = arange(y)
    return v.map_sync(mul, xa, ya)
:D
Antworten