Multiprocessing - wie richtig nutzen?

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.
hera
User
Beiträge: 18
Registriert: Sonntag 7. April 2013, 17:16

@BlackJack:
ein Minimalbeispiel läuft, das habe ich schon mit dem Code versucht, der früher mal gepostet wurde.

Deshalb wird es wohl Punkt 2 sein, dass ich versuche was zu serialisieren, was sich nicht serialisieren lässt. Da ich nicht genau weiß, was sich serialisierne lässt, beschreibe ich mal, was ich aufrufe (hier erst nochmal die Funktion von weiter vorne in den Forenbeiträgen.

Code: Alles auswählen

def solve_KW(self):
        LABE_aller_perioden = []
        pool = Pool()
        for result in pool.map(
            lambda p: ESPPRC.solve_Problem(self.MP_for_CG, p,
                                    self.instanz,
                                    self.node_id, self.iteration,
                                    self.arcs_to_forbid,
                                    self.max_labels_to_create), Node.periods_SP
        ):
            LABE_aller_perioden.extend(
                self.delete_doppelte_variable_in_sol_array(result, self.MP_for_CG))
        return LABE_aller_perioden
ESPPRC ist ein Modul mit der Funktion solve_Problem(.). Es handelt sich also nicht um ein instanziiertes Objekt einer Klasse. Heisst das, das ich diese Funktion solve_Problem(.) nicht serialisieren kann? Oder liegt es an der anonymen Funktion lambda?
BlackJack

@hera: Es liegt am ``lambda``. Funktionen werden einfach nur als der vollständige „Pfad” zu der Funktion serialisiert, also alle Paketnamen, plus der Modulname, plus der Name der Funktion. Und beim deserialisieren werden diese Informationen verwendet um die Funktion aus dem Modul zu importieren. Mal so als Beispiel:

Code: Alles auswählen

In [1]: import pickle

In [2]: from xml.etree import ElementTree as etree

In [3]: print pickle.dumps(etree.parse)
cxml.etree.ElementTree
parse
p0
.
hera
User
Beiträge: 18
Registriert: Sonntag 7. April 2013, 17:16

@BlackJack:
den code habe ich jetzt wie folgt umgebaut:

Code: Alles auswählen

    def f(self, p):
        LABE_aller_perioden = []
        result = ESPPRC.solve_Problem(self.MP_for_CG, p,
                                    self.instanz,
                                    self.node_id, self.iteration,
                                    self.arcs_to_forbid,
                                    Node.max_labels_to_create)
        LABE_aller_perioden.extend(
            self.delete_doppelte_variable_in_sol_array(result, self.MP_for_CG))
        return LABE_aller_perioden
Die Funktion über die serializiert werden soll ESPPRC.solve_Problem habe ich in eine Funktion f gesteckt mit nur einem Parameter p. Grund hierfür ist, dass mir nicht klar war, wie ich map() sonst mitteilen soll, dass es ja nur einen Laufvariable gibt über die iteriert werden soll nach Wegfall von lambda.


Aufruf von f im Hauptprogramm sieht jetzt so aus:

Code: Alles auswählen

                pool = multiprocessing.Pool()        
                print(pool.map(self.f, Node.periods_SP))
Die Fehlermeldung gibts leider immer noch:

Code: Alles auswählen

Exception in thread Thread-2:
Traceback (most recent call last):
  File "C:\Python27\lib\threading.py", line 808, in __bootstrap_inner
    self.run()
  File "C:\Python27\lib\threading.py", line 761, in run
    self.__target(*self.__args, **self.__kwargs)
  File "C:\Python27\lib\multiprocessing\pool.py", line 342, in _handle_tasks
    put(task)
PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup __builtin_
_.instancemethod failed
Siehst Du noch was, was sich in diesem Aufruf nicht serialisieren lässt neben lambda?
BlackJack

@hera: Naja `f` lässt sich nicht serialisieren, denn das ist ja eine Methode auf einem Objekt das zur Laufzeit irgendwie erzeugt wurde, und *wie* das erzeugt wurde, kann der Prozess auf der anderen Seite ja nicht nachvollziehen.
hera
User
Beiträge: 18
Registriert: Sonntag 7. April 2013, 17:16

@BlackJack:
Das heißt: f(.) aus dem Objekt rausnehmen, in ein separates Modul schreiben und dann aus dem Modul heraus und nicht aus dem Objekt heraus aufrufen?
aber halt, wie komme ich denn dann an die Parameter aus dem Objekt ran, die mit "self" aufgerufen werden?!
Zuletzt geändert von hera am Sonntag 21. Juli 2013, 17:24, insgesamt 1-mal geändert.
BlackJack

@hera: Ein separates Modul muss es nicht sein, aber halt eine Funktion die man importieren kann. Und zwar eine die ein Argument bekommt, welches alle Informationen für den Aufruf enthält. Also zum Beispiel ein Tupel mit `p` und dem Objekt wo die ganzen Attribute drauf sind:

Code: Alles auswählen

def solve((p, some_object)):
    ESPPRC.solve_Problem(
        some_object.MP_for_CG,
        p,
        some_object.instanz,
        some_object.node_id,
        some_object.iteration,
        some_object.arcs_to_forbid,
        some_object.max_labels_to_create
    )
Aufruf dann mit:

Code: Alles auswählen

    def solve_KW(self):
        LABE_aller_perioden = []
        pool = Pool()
        for result in pool.map(solve, [(p, self) for p in Node.periods_SP], 1):
            LABE_aller_perioden.extend(
                self.delete_doppelte_variable_in_sol_array(
                    result, self.MP_for_CG
                )
            )
        return LABE_aller_perioden
Falls an dem Objekt mit der `solve_KW()`-Methode deutlich mehr Daten hängen als von der `solve()`-Funktion benötigt werden, sollte man natürlich nicht das Objekt übergeben, sondern die einzelnen benötigten Attribute einzeln. Das muss ja alles serialisiert und an den Arbeiterprozess geschickt werden. Sollten dass dann immer noch zu viele Daten sein, die sich ja von Aufruf zu Aufruf nicht ändern, könnte man die beim erzeugen des Pools an den Arbeiterprozess übertragen und zum Beispiel dort an das aktuelle Prozessobjekt binden (`multiprocessing.current_process()`) und dann nur noch `p` bei dem Aufrufen übertragen.
hera
User
Beiträge: 18
Registriert: Sonntag 7. April 2013, 17:16

@BlackJack:
Vielen Dank für Deinen Post. Ich werde mich jetzt mal daran machen, das umzusetzten. Und melde mich. Gruß, HeRa
Benutzeravatar
snafu
User
Beiträge: 6736
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

hera hat geschrieben:aber halt, wie komme ich denn dann an die Parameter aus dem Objekt ran, die mit "self" aufgerufen werden?!
Wenn es *nur* die Parameter sind und dies keine Methodenaufrufe auf dem Objekt umfasst, dann kannst du diese Parameter als Dictionary übergeben. Die zu mappende Methode nimmt dann dieses Dictionary als Argument an und fragt die einzelnen Werte nach Bedarf ab.

Hier ein Beispiel zur Erzeugung eines solchen Dictionaries:

Code: Alles auswählen

>>> class MyObj(object):
...     def __init__(self, foo, bar, baz):
...         self.foo = foo
...         self.bar = bar
...         self.baz = baz
... 
>>> obj = MyObj('spam', 'ham', 'egg')
>>> attrs = ('foo', 'bar', 'baz')
>>> dict((name, getattr(obj, name)) for name in attrs)
{'baz': 'egg', 'foo': 'spam', 'bar': 'ham'}
Falls die Klasse von dir selbst definiert wurde, dann könntest du sogar eher eine Methode schreiben, damit die Klasse ihre relevanten Attribute auf dem gezeigten Wege ausspuckt. Das wäre dann noch ein bißchen sauberer.

EDIT: Oder du verwendest alternativ die Process()-Schnittstelle. Dort kann man mehrere Argumente übergeben (was in deinem Fall die entsprechenden Attribute wären). Dieser Weg ist allerdings für das Ablaufen der zu bearbeitenden Daten nicht so komfortabel wie `map()`. Ich würde wohl eher letzteres nehmen und mir halt ein `dict` erzeugen.
Antworten