Teilen Sie große, schreibgeschützte Numpy-Arrays zwischen Multiprocessing-Prozessen
Lesezeit: 10 Minuten
Wille
Ich habe ein 60-GB-SciPy-Array (Matrix), das ich zwischen 5+ teilen muss multiprocessingProcess Objekte. Ich habe numpy-sharedmem gesehen und gelesen diese Diskussion auf der SciPy-Liste. Es scheint zwei Ansätze zu geben:numpy-sharedmem und mit a multiprocessing.RawArray() und Zuordnung von NumPy dtypes zu ctypeS. Jetzt, numpy-sharedmem scheint der richtige Weg zu sein, aber ich habe noch kein gutes Referenzbeispiel gesehen. Ich brauche keine Sperren, da das Array (eigentlich eine Matrix) schreibgeschützt ist. Nun möchte ich aufgrund der Größe auf eine Kopie verzichten. Es hört sich an wie Die richtige Methode besteht darin, die zu erstellen nur Kopie des Arrays als sharedmem Array, und übergeben Sie es dann an die Process Objekte? Ein paar konkrete Fragen:
Was ist der beste Weg, um die Sharedmem-Handles tatsächlich an sub-Process()es? Benötige ich eine Warteschlange, nur um ein Array herumzureichen? Wäre ein Rohr besser? Kann ich es einfach als Argument an die übergeben Process() init der Unterklasse (wobei ich annehme, dass es eingelegt ist)?
In der Diskussion, die ich oben verlinkt habe, wird erwähnt numpy-sharedmem nicht 64bit-sicher? Ich verwende definitiv einige Strukturen, die nicht 32-Bit-adressierbar sind.
Gibt es Kompromisse zu den RawArray() sich nähern? Langsamer, fehlerhafter?
Benötige ich eine ctype-to-dtype-Zuordnung für die numpy-sharedmem-Methode?
Hat jemand ein Beispiel für einen OpenSource-Code, der dies tut? Ich bin ein sehr praktischer Gelehrter und es ist schwer, dies zum Laufen zu bringen, ohne ein gutes Beispiel zu sehen.
Wenn es zusätzliche Informationen gibt, die ich bereitstellen kann, um dies für andere zu verdeutlichen, kommentieren Sie dies bitte und ich werde es hinzufügen. Danke!
Dies muss auf Ubuntu Linux und laufen Könnte sein Mac OS, aber Portabilität ist kein großes Problem.
Wenn die verschiedenen Prozesse in dieses Array schreiben werden, erwarten Sie multiprocessing für jeden Vorgang eine Kopie des Ganzen anzufertigen.
– Tiago
22. Juli 13 um 11:33 Uhr
@tiago: “Ich brauche keinerlei Sperren, da das Array (eigentlich eine Matrix) schreibgeschützt ist”
– Dr. Jan-Philip Gehrcke
22. Juli 13 um 12:04 Uhr
@tiago: Außerdem erstellt Multiprocessing keine Kopie, solange nicht ausdrücklich dazu aufgefordert wird (über Argumente an die target_function). Das Betriebssystem wird Teile des Speichers des Elternteils nur bei Modifikation in den Speicherbereich des Kinds kopieren.
– Dr. Jan-Philip Gehrcke
22. Juli 13 um 12:11 Uhr
Hier ist ein RawArray-basiertes Beispiel, das sowohl unter * nix als auch unter Windows funktionieren sollte und auch das Schreiben in das Array unterstützt.
– jfs
24. Juli 13 um 10:19 Uhr
Dazu habe ich schon einige Fragen gestellt. Meine Lösung findest du hier: github.com/david-hoffman/peaks/blob/… (Entschuldigung, der Code ist eine Katastrophe).
– David Hoffmann
31. Juli 2020 um 5:58 Uhr
Dr. Jan-Philip Gehrcke
Unter Linux (oder einem anderen POSIX-kompatiblen System) können Sie dieses Array als globale Variable definieren. multiprocessing benutzt fork() unter Linux, wenn ein neuer untergeordneter Prozess gestartet wird. Ein neu erzeugter untergeordneter Prozess teilt den Speicher automatisch mit seinem übergeordneten Prozess, solange er ihn nicht ändert (Copy-on-Write Mechanismus).
Da Sie sagen “Ich brauche keinerlei Sperren, da das Array (eigentlich eine Matrix) schreibgeschützt ist”, wäre die Nutzung dieses Verhaltens ein sehr einfacher und dennoch äußerst effizienter Ansatz: Alle untergeordneten Prozesse werden zugreifen dieselben Daten im physischen Speicher beim Lesen dieses großen numpy-Arrays.
Übergeben Sie Ihr Array nicht an die Process() Konstrukteur, dies wird anweisen multiprocessing zu pickle die Daten an das Kind, was in Ihrem Fall äußerst ineffizient oder unmöglich wäre. Unter Linux gleich danach fork() das untergeordnete Element ist eine exakte Kopie des übergeordneten Elements, das denselben physischen Speicher verwendet, sodass Sie lediglich sicherstellen müssen, dass auf die Python-Variable, die die Matrix „enthält“, von innerhalb der zugegriffen werden kann target Funktion, an die Sie übergeben Process(). Dies können Sie typischerweise mit einer ‘globalen’ Variablen erreichen.
Beispielcode:
from multiprocessing import Process
from numpy import random
global_array = random.random(10**4)
def child():
print sum(global_array)
def main():
processes = [Process(target=child) for _ in xrange(10)]
for p in processes:
p.start()
for p in processes:
p.join()
if __name__ == "__main__":
main()
Unter Windows – was nicht unterstützt wird fork() — multiprocessing verwendet den Win32-API-Aufruf CreateProcess. Es erstellt einen völlig neuen Prozess aus einer beliebigen ausführbaren Datei. Das ist, warum auf Windows man ist erforderlich um Daten an das Kind zu picken, wenn man Daten benötigt, die während der Laufzeit des Elternteils erstellt wurden.
Copy-on-Write kopiert die Seite, die den Referenzzähler enthält (daher hat jede verzweigte Python einen eigenen Referenzzähler), aber es wird nicht das gesamte Datenarray kopiert.
– Raub
22. Juli 13 um 20:50 Uhr
Ich möchte hinzufügen, dass ich mit Variablen auf Modulebene mehr Erfolg hatte als mit globalen Variablen … dh füge die Variable vor der Abzweigung zu einem Modul im globalen Bereich hinzu
– Raub
22. Juli 13 um 20:51 Uhr
Ein Wort der Vorsicht für Leute, die über diese Frage/Antwort stolpern: Wenn Sie OpenBLAS-verlinktes Numpy für seinen Multithread-Betrieb verwenden, stellen Sie sicher, dass Sie sein Multithreading deaktivieren (exportieren Sie OPENBLAS_NUM_THREADS=1), wenn Sie es verwenden multiprocessing oder untergeordnete Prozesse könnten hängen bleiben (normalerweise mit 1/n von eins Prozessor statt n Prozessoren) beim Durchführen linearer Algebraoperationen an einem gemeinsam genutzten globalen Array/Matrix. Der bekannter Multithread-Konflikt mit OpenBLAS scheint sich auf Python auszudehnen multiprocessing
– Dolgan
31. Juli 13 um 18:27 Uhr
Kann jemand erklären, warum Python nicht nur OS verwenden würde fork gegebenen Parameter zu übergeben Process, anstatt sie zu serialisieren? Das heißt, konnte das nicht fork nur auf den übergeordneten Prozess angewendet werden Vorchild aufgerufen wird, damit der Parameterwert noch von der OS verfügbar ist? Scheint effizienter zu sein, als es zu serialisieren?
– max
22. März 15 um 20:13 Uhr
Das ist uns allen bewusst fork() ist unter Windows nicht verfügbar, es wurde in meiner Antwort und mehrfach in den Kommentaren angegeben. Ich weiß, dass dies Ihre ursprüngliche Frage war, und ich habe sie vier Kommentare weiter oben beantwortet Dies: “Der Kompromiss besteht darin, auf beiden Plattformen standardmäßig dieselbe Methode der Parameterübertragung zu verwenden, um die Wartbarkeit zu verbessern und ein gleiches Verhalten sicherzustellen.”. Beide Wege haben ihre Vor- und Nachteile, weshalb in Python 3 eine größere Flexibilität für den Benutzer bei der Wahl der Methode besteht. Diese Diskussion ist nicht produktiv, ohne Details zu besprechen, was wir hier nicht tun sollten.
– Dr. Jan-Philip Gehrcke
23. März 15 um 18:49 Uhr
James Lim
@Velimir Mlaker hat eine tolle Antwort gegeben. Ich dachte, ich könnte einige Kommentare und ein kleines Beispiel hinzufügen.
(Ich konnte nicht viel Dokumentation zu Sharedmem finden – dies sind die Ergebnisse meiner eigenen Experimente.)
Müssen Sie die Handles übergeben, wenn der Unterprozess gestartet wird oder nachdem er gestartet wurde? Wenn es nur das erstere ist, können Sie einfach das verwenden target und args Argumente für Process. Dies ist möglicherweise besser als die Verwendung einer globalen Variablen.
Aus der von Ihnen verlinkten Diskussionsseite geht hervor, dass Sharedmem vor einiger Zeit Unterstützung für 64-Bit-Linux hinzugefügt wurde, sodass dies möglicherweise kein Problem darstellt.
Ich weiß nichts über diesen.
Nr. Siehe Beispiel unten.
Beispiel
#!/usr/bin/env python
from multiprocessing import Process
import sharedmem
import numpy
def do_work(data, start):
data[start] = 0;
def split_work(num):
n = 20
width = n/num
shared = sharedmem.empty(n)
shared[:] = numpy.random.rand(1, n)[0]
print "values are %s" % shared
processes = [Process(target=do_work, args=(shared, i*width)) for i in xrange(num)]
for p in processes:
p.start()
for p in processes:
p.join()
print "values are %s" % shared
print "type is %s" % type(shared[0])
if __name__ == '__main__':
split_work(4)
Die einzige Datei von Interesse ist main.py. Es ist ein Maßstab für numpy-sharedmem — der Code übergibt einfach Arrays (entweder numpy oder sharedmem) zu gespawnten Prozessen über Pipe. Die Arbeiter rufen einfach an sum() auf den Daten. Ich war nur daran interessiert, die Datenkommunikationszeiten zwischen den beiden Implementierungen zu vergleichen.
Hier verwende ich die numpy-sharedmem Modul für die Echtzeit-Bildverarbeitung mit OpenCV – die Bilder sind NumPy-Arrays, gemäß den neueren von OpenCV cv2 API. Die Bilder, eigentlich Verweise darauf, werden zwischen Prozessen über das erstellte Wörterbuchobjekt geteilt multiprocessing.Manager (im Gegensatz zur Verwendung von Queue oder Pipe.) Ich erhalte große Leistungsverbesserungen im Vergleich zur Verwendung von einfachen NumPy-Arrays.
Pipe vs. Warteschlange:
Meiner Erfahrung nach ist IPC mit Pipe schneller als Queue. Und das macht Sinn, da Queue Sperren hinzufügt, um es für mehrere Produzenten/Konsumenten sicher zu machen. Rohr nicht. Aber wenn Sie nur zwei Prozesse haben, die hin und her sprechen, ist es sicher, Pipe zu verwenden, oder, wie es in der Dokumentation heißt:
… es besteht keine Verfälschungsgefahr durch Prozesse, die gleichzeitig verschiedene Rohrenden verwenden.
sharedmem Sicherheit:
Das Hauptproblem mit sharedmem Modul ist die Möglichkeit eines Speicherlecks bei unspektakulärem Beenden des Programms. Dies wird in einer längeren Diskussion beschrieben Hier. Obwohl Sturla am 10. April 2011 einen Fix für ein Speicherleck erwähnt, habe ich seitdem immer noch Lecks erlebt, indem ich beide Repos verwende, Sturla Moldens eigenen auf GitHub (github.com/sturlamolden/sharedmem-numpy) und Chris Lee-Messer auf Bitbucket (bitbucket.org/cleemesser/numpy-sharedmem).
Danke, sehr sehr informativ. Die Erinnerung sickert ein sharedmem hört sich aber nach einer großen Sache an. Irgendwelche Anhaltspunkte, um das zu lösen?
– Wille
29. Juli 13 um 5:18 Uhr
Abgesehen davon, dass ich nur die Lecks bemerkt habe, habe ich nicht im Code danach gesucht. Ich habe zu meiner Antwort oben unter “Sharedmem-Sicherheit” die Bewahrer der beiden Open-Source-Repos der sharedmem Modul als Referenz.
– Velimir Mlaker
29. Juli 13 um 17:54 Uhr
Saullo GP Castro
Wenn Ihr Array so groß ist, können Sie verwenden numpy.memmap. Wenn Sie beispielsweise ein Array auf der Festplatte gespeichert haben, sagen Sie 'test.array'können Sie gleichzeitige Prozesse verwenden, um auf die darin enthaltenen Daten auch im “Schreibmodus” zuzugreifen, aber Ihr Fall ist einfacher, da Sie nur den “Lesemodus” benötigen.
Erstellen des Arrays:
a = np.memmap('test.array', dtype="float32", mode="w+", shape=(100000,1000))
Sie können dieses Array dann genauso füllen wie ein gewöhnliches Array. Beispielsweise:
a[:10,:100]=1.
a[10:,100:]=2.
Die Daten werden auf der Festplatte gespeichert, wenn Sie die Variable löschen a.
Später können Sie mehrere Prozesse verwenden, die auf die Daten zugreifen test.array:
# read-only mode
b = np.memmap('test.array', dtype="float32", mode="r", shape=(100000,1000))
# read and writing mode
c = np.memmap('test.array', dtype="float32", mode="r+", shape=(100000,1000))
Verwandte Antworten:
Arbeiten mit Big Data in Python und Numpy, nicht genug RAM, wie kann man Teilergebnisse auf Disc speichern?
Ist es möglich, diskontinuierliche Daten auf der Festplatte einem Array mit Python zuzuordnen?
Es kann auch hilfreich sein, einen Blick in die Dokumentation für zu werfen Pyro Als ob Sie Ihre Aufgabe entsprechend partitionieren könnten, könnten Sie damit verschiedene Abschnitte auf verschiedenen Maschinen sowie auf verschiedenen Kernen derselben Maschine ausführen.
Warum nicht Multithreading verwenden? Ressourcen des Hauptprozesses können von seinen Threads nativ gemeinsam genutzt werden, daher ist Multithreading offensichtlich eine bessere Möglichkeit, Objekte gemeinsam zu nutzen, die dem Hauptprozess gehören.
Wenn Sie sich Sorgen um den GIL-Mechanismus von Python machen, können Sie vielleicht auf den zurückgreifen nogil von numba.
.
6198000cookie-checkTeilen Sie große, schreibgeschützte Numpy-Arrays zwischen Multiprocessing-Prozessenyes
Wenn die verschiedenen Prozesse in dieses Array schreiben werden, erwarten Sie
multiprocessing
für jeden Vorgang eine Kopie des Ganzen anzufertigen.– Tiago
22. Juli 13 um 11:33 Uhr
@tiago: “Ich brauche keinerlei Sperren, da das Array (eigentlich eine Matrix) schreibgeschützt ist”
– Dr. Jan-Philip Gehrcke
22. Juli 13 um 12:04 Uhr
@tiago: Außerdem erstellt Multiprocessing keine Kopie, solange nicht ausdrücklich dazu aufgefordert wird (über Argumente an die
target_function
). Das Betriebssystem wird Teile des Speichers des Elternteils nur bei Modifikation in den Speicherbereich des Kinds kopieren.– Dr. Jan-Philip Gehrcke
22. Juli 13 um 12:11 Uhr
Hier ist ein RawArray-basiertes Beispiel, das sowohl unter * nix als auch unter Windows funktionieren sollte und auch das Schreiben in das Array unterstützt.
– jfs
24. Juli 13 um 10:19 Uhr
Dazu habe ich schon einige Fragen gestellt. Meine Lösung findest du hier: github.com/david-hoffman/peaks/blob/… (Entschuldigung, der Code ist eine Katastrophe).
– David Hoffmann
31. Juli 2020 um 5:58 Uhr