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

so:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Users\hera\workspace\my_Prog\program_control.py", line 746, in <module>
    lauf(data) 
  File "C:\Users\hera\workspace\my_Prog\program_control.py", line 232, in lauf
    root_node.cg(instanz_name, None, max_labels_to_create)
  File "C:\Users\hera\workspace\my_Prog\Node.py", line 157, in cg
    LABE_aller_perioden = self.solve_KW()
  File "C:\Users\hera\workspace\my_Prog\Node.py", line 121, in solve_KW
    pool = Pool()
  File "C:\Python27\lib\multiprocessing\__init__.py", line 232, in Pool
    return Pool(processes, initializer, initargs, maxtasksperchild)
  File "C:\Python27\lib\multiprocessing\pool.py", line 115, in __init__
    self._setup_queues()
  File "C:\Python27\lib\multiprocessing\pool.py", line 209, in _setup_queues
    from .queues import SimpleQueue
  File "C:\Python27\lib\multiprocessing\queues.py", line 48, in <module>
    from multiprocessing.synchronize import Lock, BoundedSemaphore, Semaphore, Condition
  File "C:\Python27\lib\multiprocessing\synchronize.py", line 48, in <module>
    from multiprocessing.forking import assert_spawning, Popen
  File "C:\Python27\lib\multiprocessing\forking.py", line 58, in <module>
    from pickle import Pickler
ImportError: cannot import name Pickler
BlackJack

@hera: Führ mal in einem Skript ``import pickle; print pickle`` aus. Was wird dann ausgegeben?
hera
User
Beiträge: 18
Registriert: Sonntag 7. April 2013, 17:16

@BlackJack, das:

Code: Alles auswählen

<module 'pickle' from 'C:\Python27\lib\pickle.pyc'>
BlackJack

@hera: Da sollte dann aber auch ein Objekt unter dem Namen `Pickler` enthalten sein. Und was wird ausgegeben wenn Du das irgendwo in Deinem Code vor dem Fehler platzierst?
hera
User
Beiträge: 18
Registriert: Sonntag 7. April 2013, 17:16

@BlackJack:
unmittelbar vor pool = Pool() habe ich nun print(pickle) aufgerufen. Da passt die Ausgabe noch, das import pickle wurde erkannt. Innerhalb von Pool() wirds dann nicht mehr erkannt, obwohl pickle im modul multiprocessing separat aufgerufen wird. Ist denn allgemein der Aufruf von map() nun an der richtigen Stelle, also innerhalb der Funktion, die in mehrere Prozesse ausgelagert werden soll?
BlackJack

@hera: Also Du bekommst dort den gleichen Pfad vom `pickle`-Modul ausgegeben? Mein Verdacht ist/war ja, dass Du da noch irgendwo ein Modul hast, was `pickle` heisst, aber eben ein anderes ist. Was wird denn direkt vor der `Pool`-Zuweisung ausgegeben wenn Du ``print dir(pickle)`` ausführst? Sind da die erwarteten Namen ausser dem `Pickler` oder ganz was anderes?

Startest Du das eigentlich auf der Konsole, oder aus einer IDE heraus? IDEs sind auch gerne mal Fehlerquellen, wenn sie die Laufzeitumgebung von Python beeinflussen.

`map()` wendet eine Funktion auf die einzelnen Elemente einer Liste (bzw. allgemein ein iterierbares Objekt) an und erzeugt eine Liste mit den Ergebnissen dieser Aufrufe. `multiprocessing.Pool.map()` führt dabei die Funktion auf verschiedenen Elementen parallel in verschiedenen Prozessen aus.
hera
User
Beiträge: 18
Registriert: Sonntag 7. April 2013, 17:16

Hallo BlackJack,

wenn ich print(dir(pickle)) aufrufe, wird folgendes ausgegeben:
['__builtins__', '__doc__', '__file__', '__name__', '__package__']
Hilft das?
Ansonsten gibts dann dieselbe Fehlermeldung wie zuvor gepostet.

Ich starte aus eclipse, gab bislang aber auch noch nie Probleme - was ja nix heißen mag.

Danke für die Erläuterung zur Funktion map(), habe ich soweit inzwischen durchschaut. Für mich war mehr die Frage, ob es denn nun auch an der richtigen Stellt steht, also innerhalb der Funktion, die in parallelen Prozessen gestartet werden soll.

Gruß, HeRa
BlackJack

@hera: Und bei der Pfadangabe wenn Du das Modul selbst ``print``\est ist auch der Pfad zur Standardbibliothek? Das ist sehr eigenartig.

Und natürlich ist die Funktion die parallel abgearbeitet werden soll nicht der Ort um `map()` zu verwenden, denn `map()` muss man ja genau die Funktion als Argument übergeben, die dann parallel ausgeführt wird. Da müsste man dann ja die Funktion die das `map()` enthält übergeben, damit die sich selber parallel ausführt und dabei dann sich selbst an `map()` übergibt, und dann parallel ausgeführt wird, wobei dort die Funktion an `map()` übergeben wird, um dann parallel ausgeführt zu werden, wobei…

*So* bekommt man auch unendlich viele Prozesse hin, beziehungsweise bis zu der Grenze wo das Betriebssystem darauf keine Lust mehr hat. ;-)
hera
User
Beiträge: 18
Registriert: Sonntag 7. April 2013, 17:16

@BlackJack:
ja, genau genommen siehts von den Aufrufen jetzt so aus, dass die Funktion, die parallelisiert aufgerufen werden soll, in map() übergeben wird. Ich vermute mal, dass ist jetzt der Weg, wie map() zu nutzen ist (ok, so ganz sicher bin ich mir da natürlich nicht).

Was könnte ich denn noch an Infos liefern, um über dieses eigenartige Verhalten und zur Parallelisierung beizutragen. Wäre froh, zu einer Lösung finden zu können.
hera
User
Beiträge: 18
Registriert: Sonntag 7. April 2013, 17:16

Eine weitere Sache ist mir aufgefallen.
In der Fehlermeldung wird ja forking.py aufgelistet. forking.py enthält folgenden Code:

Code: Alles auswählen

from pickle import Pickler
print(Pickler)
Wenn ich nun versuche ausschließlich diesen Code auszuführen, gibts dieselbe Fehlermeldung:

Code: Alles auswählen

from pickle import Pickler
ImportError: cannot import name Pickler
Frage nun, warum findet er den Pickler nicht? Und was macht der Pickler eigentlich?
BlackJack

@hera: Wo kommt denn das ``print(Pickler)`` her? Das gehört da ziemlich sicher nicht hinein.

Du hast auch immer noch nicht verraten was ``import pickle; print pickle`` ausgibt wenn Du das in Deinem Code vor die Zeile platzierst, welche zu der Ausnahme führt. Denn wenn das Modul leer ist, kann es ja nicht das aus der Standardbibliothek sein. Und das ganze bitte ohne Eclipse ausführen.
hera
User
Beiträge: 18
Registriert: Sonntag 7. April 2013, 17:16

@BlackJack:
ich habe den Code jetzt mal nicht aus der IDE aus gestartet, sondern direkt. Das Problem mit dem Pickler tritt dann nicht auf.

Jetzt erhalte ich diese Fehlermeldung, die ja sehr aussagekräftig ist und den Hinweis mit freeze_support() enthält:

Code: Alles auswählen

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Python27\lib\multiprocessing\forking.py", line 380, in main
    prepare(preparation_data)
  File "C:\Python27\lib\multiprocessing\forking.py", line 495, in prepare
    '__parents_main__', file, path_name, etc
  File "C:\myProg\program_control.py", line 746, in <module>
    lauf(data)
  File "C:\myProg\program_control.py", line 232, in lauf
    rn.cg(instanz_name, None, max_labels_to_create)
  File "C:\myProg\Node.py", line 159, in cg
    LABE_aller_perioden = self.solve_KW(arcs_to_forbid, max_labels_to_create)
  File "C:\myProg\Node.py", line 121, in solve_KW
    pool = multiprocessing.Pool()
  File "C:\Python27\lib\multiprocessing\__init__.py", line 232, in Pool
    return Pool(processes, initializer, initargs, maxtasksperchild)
  File "C:\Python27\lib\multiprocessing\pool.py", line 159, in __init__
    self._repopulate_pool()
  File "C:\Python27\lib\multiprocessing\pool.py", line 222, in _repopulate_pool
    w.start()
  File "C:\Python27\lib\multiprocessing\process.py", line 130, in start
    self._popen = Popen(self)
  File "C:\Python27\lib\multiprocessing\forking.py", line 258, in __init__
    cmd = get_command_line() + [rhandle]
  File "C:\Python27\lib\multiprocessing\forking.py", line 358, in get_command_li
ne
    is not going to be frozen to produce a Windows executable.''')
RuntimeError:
            Attempt to start a new process before the current process
            has finished its bootstrapping phase.

            This probably means that you are on Windows and you have
            forgotten to use the proper idiom in the main module:

                if __name__ == '__main__':
                    freeze_support()
                    ...

            The "freeze_support()" line can be omitted if the program
            is not going to be frozen to produce a Windows executable.
Aber: ich habe keine __main__ in meinem Programm drin. Python rufe ich direkt über eine batch auf mit Übergabeparametern.
Muss if __name__ == '__main__' im Programm enthalten sein, um freeze_support zu nutzen?

Habe den freeze_support() Befehl mal direkt vor

Code: Alles auswählen

        multiprocessing.freeze_support()
        pool = multiprocessing.Pool()
gesetzt.
Das hat jetzt keine Abhilfe geschaffen, die Fehlermeldung taucht weiterhin auf.
Zuversichtlich stimmt mich jetzt aber: Wenn ich den Prozessverlauf im Taskmanager anschaue bleibts aber jetzt schon bei den maximal 7 Aufrufen und die Prozesse verschwinden nach der Ausführung, also keine Endlosschleife mehr.

Was könnte ich noch ändern?
BlackJack

@hera: Da Du cxfreeze ja gar nicht benutzt, sollte das mit dem Freeze-Support nicht nötig sein. ``if __name__ == '__main__':`` muss im Grunde schon vorhanden sein, oder wie stellst Du sonst sicher, dass das Modul mit dem das Programm gestartet wird, ohne Seiteneffekte importiert werden kann?
hera
User
Beiträge: 18
Registriert: Sonntag 7. April 2013, 17:16

habe nun __name__ == '__main__' mit aufgenommen und erhalte diese Meldung:

Code: Alles auswählen

C:\myProg>"C:\myProg\program_control.py"
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 'function'>: attribute lookup __builtin__.func
tion failed
Ich weiss nicht, wie ich Seiteneffekte vermeiden kann...
BlackJack

@hera: Vielleicht sollte man das von beiden Seiten angehen. Also nicht nur Deinen Code mit `multiprocessing` zum Laufen bringen, sondern erst einmal `multiprocessing` mit einem minimalen Beispiel zum Laufen bringen:

Code: Alles auswählen

from multiprocessing import Pool
from time import sleep


def do_something(value):
    sleep(0.5)
    return value * 2


def main():
    pool = Pool()
    print pool.map(do_something, range(10))


if __name__ == '__main__':
    main()
Die Seiteneffekte verhinderst Du durch die ``if``-Abfrage. Du kannst das Modul importieren ohne das der Code in dem ``if``-Block ausgeführt wird.

Zu der neuen Fehlermeldung: Offensichtlich versuchst Du irgendetwas zu verwenden was sich nicht mit `pickle` serialisieren lässt. Also bekommt man das auch nicht in den anderen Prozess. Ich hatte ja schon Zweifel angemeldet ob man als erstes Argument in diesem Fall überhaupt eine Methode verwenden kann. Anonyme Funktionen funktionieren auch nicht — die führen zum Beispiel genau zu der angegebenen Ausnahme.
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.
Antworten