Mehr

Kann Multiprocessing mit arcpy in einem Skript-Tool ausgeführt werden?


Ich habe mit dem Multiprocessing-Modul herumgebastelt, um einige Datenverarbeitungen in ArcMap 10.3 aufzulösen.

Ich habe ein Skript erstellt, das funktioniert, wenn ich es im IDLE ausführe. Ich sehe im Task-Manager alle meine Kerne maximal und der Code wird ohne Fehler abgeschlossen.

Wenn ich dieses Skript nun als Skriptwerkzeug in ArcToolbox verdrahte, wird ein seltsamer Fehler ausgegeben

Datei konnte nicht gefunden werden: from multiprocessing.forking import main; main().mxd

Wenn ich jetzt verschiedene Threads lese, sehe ich regelmäßig, dass das Skript "aus dem Prozess laufen" muss. Also habe ich das Kontrollkästchen "Python in Prozess ausführen" im Dialogfeld mit den Skripteigenschaften deaktiviert und erwartet, dass der Code ausgeführt wird, aber dies geschieht nicht, ich erhalte einen Fehler 000714 und habe nichts am Code geändert.

Meine Frage ist also, kann man einfach ein Skript erstellen, das das Multiprocessing-Modul verwendet und es von ArcToolbox aus ausführen? Von meinem begrenzten Herumspielen scheint ich es nicht zu können und es ist eine Technik, die nur von IDLE ausgeführt werden kann?


Ich habe es endlich zum Laufen gebracht, mit jeder Hilfe. Ich beschloss, mein einfaches Beispiel vollständig zu dokumentieren und alle Trip-Ups, die ich überwinden musste, in einem Dokument auf der ESRI GeoNet-Website zusammenzufassen. Wie Lukes Antwort hoffe ich, dass dies einen Ausgangspunkt für jeden bietet, der versucht, ein Python-Skript-Tool zu erstellen, das Multiprocessing verwendet. Das Dokument trägt den Titel Erstellen Sie ein Skriptwerkzeug, das Multiprocessing verwendet.


Ja, Sie können untergeordnete Multiprocessing-Prozesse über ein Toolbox-Skript ausführen. Unten finden Sie einige Codes zur Demonstration in einer Python-Toolbox (*.pyt).

Es gibt eine Reihe von "Fallen". Einige (aber nicht alle) werden auf Python-Skripttools in einer binären Toolbox (*.tbx) anwendbar sein, aber ich verwende heutzutage nur Python-Toolboxen und habe es daher nicht getestet.

Einige "Fallstricke"/Tipps:

  • Stellen Sie sicher, dass jeder untergeordnete Prozess über einen eigenen Arbeitsbereich für temporäre Dateien verfügt (insbesondere bei Verwendung von Spatial Analyst oder Coverage-Tools) und verwenden Sie nur den Hauptprozess, um die Arbeit zuzuweisen, die Ergebnisse zu sammeln und die endgültigen Ergebnisse zu schreiben. Auf diese Weise wird jedes Schreiben in einen endgültigen Datensatz von einem Prozess ausgeführt, wodurch Sperren oder Konflikte vermieden werden.
  • Übergeben Sie nur beizbare Objekte wie Strings, Listen, Zahlen;
  • Alle Funktionen, die Sie als untergeordneter Prozess ausführen möchten, MÜSSEN sich in einem importierbaren Modul befinden (mit einem anderen Dateinamen als .pyt), nicht in .pyt selbst. Sonst bekommst duPicklingError: Kann nicht einlegen : Attributsuche __builtin__.function fehlgeschlagen;
  • Stellen Sie sicher, dass sich der Code, der das Multiprocessing ausführt, in einem importierbaren Modul befindet (mit einem anderen Dateinamen als .pyt), nicht in .pyt selbst. Sonst bekommst duAssertionError: main_name nicht in sys.modules, main_name

Gilt für*.pySkripte in einer binären Toolbox (*.tbx):

  • Stellen Sie sicher, dass der Code, der die Skriptparameter analysiert, durch a . geschützt istif __name__ == '__main__':Block.
  • Sie können die Funktionen, die Sie aufrufen möchten, im .py-Skript beibehalten, aber Sie müssen das Skript in sich selbst importieren.

Beispielcode

Python-Toolbox

# test_multiprocessing.pyt import os, sys import multiprocessing import arcpy # Alle Funktionen, die Sie als Kindprozess ausführen möchten, MÜSSEN sich in einem # importierbaren Modul befinden. Eine *.pyt kann von Python nicht importiert werden # Andernfalls erhalten Sie # PicklingError: Can't pickle : Attributsuche __builtin__.function failed # Stellen Sie außerdem sicher, dass der Code, der das Multiprocessing _ausführt_, in einem importierbaren Modul ist # Andernfalls erhalten Sie # AssertionError: main_name not in sys.modules, main_name from test_multiprocessing_functions import Execute class Toolbox(object): def __init__(self):"Definiere Toolbox-Eigenschaften (der Name der Toolbox ist der .pyt-Dateiname)."self.label = 'Test Multiprocessing' self.alias = 'multiprocessing' # Liste der mit dieser Toolbox verknüpften Werkzeugklassen self.tools = [ TestTool] Klasse TestTool(object): def __init__(self): self.label = 'Test Multiprocessing' self.description = 'Test Multiprocessing Tool' self.canRunInBackground = True self.showCommandWindow = False def isLicensed(self): Rückgabe True def updateParameters(self, parameters): return def updateMessages(self, parameters): return def getParameterInfo(self):"Parameterdefinitionen für GUI"return [arcpy.Parameter(displayName='Input Rasters', name="in_rasters", datatype= "DERasterDataset", ParameterT ype="Required", direction="Input", multiValue=True)] def execute(self, parameters, messages): # Stellen Sie sicher, dass sich der Code, der die Multiverarbeitung _ausführt_, in einem importierbaren Modul befindet, nicht in einer .pyt # Andernfalls Ich bekomme # AssertionError: main_name not in sys.modules, main_name rasters = parameters[0].valueAsText.split(';') für Raster in Rastern: Messages.addMessage(raster) execute(*rasters)

Importierbares Python-Modul (kann auch als Skript-Tool in einer binären Toolbox (.tbx) verwendet werden

#test_multiprocessing_functions.py # - Immer im Vordergrund ausführen - nicht markiert # - Python-Skript im Prozess ausführen - aktiviert import os, sys, tempfile import multiprocessing import arcpy von arcpy.sa import * def execute(*rasters): für Raster in Rastern: arcpy .AddMessage(raster) #Set multiprocessing exe, falls wir als eingebetteter Prozess ausgeführt werden, dh ArcGIS #get_install_path() verwendet eine Registrierungsabfrage, um die 64-Bit-Python-exe herauszufinden, falls verfügbar multiprocessing.set_executable(os.path.join(get_install_path( ), 'pythonw.exe')) #Erstellen Sie einen Pool von Arbeitern, halten Sie eine CPU frei, um im Internet zu surfen. #Jeden Worker-Prozess vor dem Neustart nur 10 Aufgaben bearbeiten lassen (im Falle von bösen Speicherlecks) pool = multiprocessing.Pool(processes=multiprocessing.cpu_count() - 1, maxtasksperchild=10) # Am einfachsten ist Multiprocessing, ein iterierbares (dh eine Liste der zu verarbeitenden Dinge) an eine Funktion # Aber dies erlaubt Ihnen nicht, Ausnahmen in einem einzigen Prozess zu behandeln ##output_rasters = pool.map(worker_function, rasters) # Verwenden Sie stattdessen apply_async, damit wir Ausnahmen elegant behandeln können jobs={ } für Raster in Rastern: jobs[raster]=pool.apply_async(worker_function, [raster]) # Argumente werden als Liste für raster,result in jobs.iteritems() übergeben: try: result = result.get() arcpy. AddMessage(result) außer Ausnahme als e: arcpy.AddWarning('{}
{}'.format(raster, repr(e))) pool.close() pool.join() def get_install_path():"Return 64bit Python-Installationspfad aus der Registrierung (sofern installiert und registriert), andernfalls auf den aktuellen 32-Bit-Prozessinstallationspfad zurückgreifen."if sys.maxsize > 2**32: return sys.exec_prefix #Wir sind läuft in einem 64-Bit-Prozess #Wir sind 32-Bit, also sehen Sie, ob es einen 64-Bit-Installationspfad gibt = r'SOFTWAREPythonPythonCore2.7' from _winreg import OpenKey, QueryValue from _winreg import HKEY_LOCAL_MACHINE, KEY_READ, KEY_WOW64_64KEY try: with OpenKey_MACHINE_LOCAL , Pfad, 0, KEY_READ | KEY_WOW64_64KEY) als Schlüssel: return QueryValue(key, "InstallPath").strip(os.sep) #Wir haben eine 64-Bit-Installation, also gib das zurück. außer: return sys.exec_prefix #No 64bit, also return 32bit path def worker_function(in_raster):"Stellen Sie sicher, dass Sie einen Dateipfad an Raster übergeben, NICHT an ein arcpy.sa.Raster-Objekt"## Beispiel "echte" Arbeit" (ungetestet) ## Einen einzigartigen Scratch-Workspace erstellen #scratch = tempfile.mkdtemp() #out_raster = os.path.join(scratch, os.path.basename(in_raster)) #arcpy.env.workspace = scratch #arcpy.env.scratchWorkspace= scratch #ras = Raster(in_raster) #result = Con(IsNull(ras), FocalStatistics(ras), ras) #result.save(out_raster) #del ras, result #return out_raster # Aufrufendes Skript verlassen, um tempdir zu bereinigen. # könnte auch out_raster als Argument übergeben, # aber Sie müssen sicherstellen, dass keine anderen untergeordneten Prozesse # das Verzeichnis schreiben, wenn # der aktuelle untergeordnete Prozess… # "Fake"-Arbeit importieren Zeit, zufällige Zeit.sleep( random.randint(0,20)/10.0) #sleep für ein bisschen, um die Arbeit zu simulieren return in_raster[::-1] #Gibt eine umgekehrte Version von dem zurück, was übergeben wurde if __name__=='__main__': # importiere das aktuelle Skript nach vermeiden: # PicklingError : Kann nicht einlegen : Attributsuche __builtin__.function fehlgeschlagener Import test_multiprocessing_functions rasters = arcpy.GetParameterAsText(0).split(';') für Raster in Rastern: arcpy.AddMessage(raster) test_multiprocessing_functions.execute(*rasters)

Pool aus Multiprocessing-Problemen

Ich versuche, Pool aus dem Multiprocessing zu verwenden, um einige Operationen def worker(d) zu beschleunigen, die einmal für jeden Leyer in der mxd ausgeführt werden. dieses ist fest in D:TEMPUntitled4.mxd codiert. es läuft, aber nur eins nach dem anderen. Ich kann sehen, dass es den Pool startet, aber nur on wird verwendet. jede hilfe wäre toll. Ich führe es in der Arctool-Box in ArcMap aus und habe die Option "Als Prozess ausführen" deaktiviert. wie gesagt es läuft, aber immer nur einzeln.

arcpy importieren
Importieren von OS
Multiprocessing importieren

def Arbeiter(d):
# Pufferschicht durch die folgenden Werte
bfs = [101, 200, 201, 400, 401, 750, 751, 1001,
1500, 1501, 2000, 2001, 2500]
für bf in bfs:
Ausgabe = os.path.basename(d)[:-4] + "_Buffer" + str(bf) + ".shp"
print "Puffer" + d + " at " + str(bf) + " Feet"
if arcpy.Exists(d):
arcpy.Buffer_analysis(d, "D:Temp" + Ausgabe, str(bf) + "Fuß")
arcpy.Project_management("D:Temp" + Ausgabe, "D:TempTest" + Ausgabe, "C:Program Files (x86)ArcGISDesktop10.0Coordinate Systems Geographische KoordinatensystemeNordamerikaNAD 1983.prj")
arcpy.Delete_management("D:Temp" + Ausgabe)
sonst:
"Keine Daten" drucken


if __name__ == '__main__':

#Sets MXD
mxd = arcpy.mapping.MapDocument("D:TEMPUntitled4.mxd")
#mxd = arcpy.mapping.MapDocument("AKTUELL")

#set einige Umgebungen, die benötigt werden, um die richtigen Ausgaben zu erhalten
arcpy.env.overwriteOutput = True
arcpy.env.workspace = "D:TEMPTest"
arcpy.env.outputCoordinateSystem = "C:Program Files (x86)ArcGISDesktop10.0Coordinate SystemsProjected Coordinate SystemsUTMNAD 1983NAD 1983 UTM Zone 16N.prj"

Anzahl der zu verwendenden Prozessoren auf max minus 1 eingestellt
prc = int(os.environ["NUMBER_OF_PROCESSORS"]) - 1

# Erstellen und starten Sie einen Pool von Worker-Prozessen
pool = multiprocessing.Pool(prc)

# Ruft alle Ebenen im aktuellen MXD ab
lyrs = arcpy.mapping.ListLayers(mxd)

#Durchläuft jede Ebene und ruft den Namen und Pfad der Quelldaten ab data
für lyr in lyr:
d = lyr.dataSource
print "Übergabe " + d + " an Verarbeitungspool"
arcpy.AddMessage("Übergeben von " + d + " an Verarbeitungspool")
pool.apply_async(worker(d))


  • Um Ihren Code leichter anzuzeigen, wählen Sie beim Einfügen Ihren gesamten Code aus und klicken Sie oben auf die Schaltfläche # - dies wird als Code angezeigt, sodass Ihre Einrückung beibehalten wird.

  • Versuchen Sie, prc und lyrs vor der Multiprocessing-Schleife zu drucken, mit arcpy.AddMessage(str(prc)+' - '+str(len(lyrs))' - '+str(lyrs)), damit Sie überprüfen können, ob sie wie Sie aussehen würde erwarten.

  • Ich weiß nicht genau, wie das Multiprocessing-Ding funktioniert, aber ich habe es ein bisschen benutzt. Meiner Erfahrung nach müssen Sie die Jobs einem Jobserver (nur einer Python-Liste) hinzufügen und von jedem Arbeiter ein Ergebnis erhalten. Ich habe Ihren Code so geändert, dass jeder Worker eine Zeichenfolge zurückgibt und dann eine Nachricht ausgibt, die Ihnen mitteilt, wenn jeder Worker fertig ist.

Lass mich wissen, wie es dir geht.

Danke für die Hilfe, an der ich ab und zu 2 Tage gearbeitet habe, da ich von mehreren Dingen profitieren kann, indem ich einen Prozess oder jede Schicht in einer mxd habe. Ich habe einen MP aufgegeben und die PP-Python verwendet. Ich habe Ihr Projekt als Beispiel verwendet http://pythongisandstuff.wordpress.com/author/stacyrendall/ . Ich hatte auch ein Installationsproblem mit ArcView, das meine Probleme verschlimmerte. Der folgende Code, den ich gerade fertiggestellt habe, puffert die Ebenen in einem gespeicherten mxd. Ich habe es als einzelnen Thread ausgeführt und es dauerte 420 Sekunden. Ich habe die pp-Bibliothek verwendet und es dauerte 151 Sekunden von Python und 154 als Werkzeug, also eine erhebliche Verbesserung. Ich konnte nie die Jobs bekommen, um mit mp zu beginnen, jetzt, da ich etwas Verständnis für die pp habe, denke ich, dass ich dabei bleiben werde.

Ich habe deine Modifikation getestet. I es läuft immer noch als ein einzelner Prozess. Vielen Dank für Ihre Hilfe, Ihre Webseite war sehr nützlich

Ich schätze, es gibt nur einen sicheren Weg, dies zu sagen, aber ? Habt ihr Ideen, wie die Speichernutzung für Multiprozess- oder paralleles Python verteilt und/oder recycelt wird? In früheren Zeiten waren viele von uns gezwungen, separate Prozesse (eine separate Python .exe) mit spawnv oder Subprozessen zu erstellen. In erster Linie lag dies einfach daran, dass das gp-Objekt (oder arcpy) einige wirklich schlimme Speicherverlustprobleme hatte und jeder gp-fähigen Anwendung (python.exe) allmählich der Speicher ausging, wenn Tools in einer Schleife ausgeführt wurden. Als zusätzlichen Bonus können Sie zusätzlichen Code schreiben, um die separaten Prozesse als Pseudo-Parallel-Prozess zu verwalten. Diese Probleme mit gp/arcpy-Speicherlecks wurden stark verbessert, sind aber immer noch ein erhebliches Problem für die ??Heavy-Duty ? -Geoverarbeitung ?

Wie auch immer, ich bin gespannt, ob pp oder Multiprocess diese Speicherleckprobleme irgendwie vermeiden können. Im pp-Beispiel sehe ich, dass Sie dem parallelen Prozess dasselbe arcpy-Objekt übergeben, das in main instanziiert wird. Verwenden alle untergeordneten Prozesse dieselbe arcpy-Instanz (und tragen vermutlich zum Speicherverbrauch bei) bei? Da ich im Beispiel von mutiprocess I keinen expliziten Code wie im pp-Beispiel sehe, gehe ich davon aus, dass die untergeordneten Prozesse standardmäßig nur Zugriff auf alle globalen Variablen in main haben.

Ich vermute, dass PP und/oder Multiprocessing immer noch unter einem allmählichen und katastrophalen gp/arcpy-Speicherverbrauch leiden werden, aber ich kann es kaum erwarten, einige Dinge selbst auszuprobieren. Tausend Dank für diese Beispiele.

Chris in meinem PP-Beispiel verwende ich "In_memory", das einen Teil Ihres RAM verwendet, um Daten zu speichern, um die parallel laufenden Wrker-Funktionen zu beschleunigen. Jeder "Job" erstellt ein eindeutiges "In_memory"-Objekt, also stellen Sie sicher, dass Sie es löschen, bevor die Wrker-Funktion endet. Was Programmspeicherlecks angeht, gehe ich davon aus, dass sie alle noch da sind und ein viel besseres Problem wäre, wenn der Code als einzelner Prozess ausgeführt würde laufen auf Speicherprobleme, aber ich bin nicht einmal annähernd an meine Sachen herangekommen.

Der pp-Code (nur einer, den ich arbeiten musste) muss aus dem Prozess laufen, also legt "prc" in meinem Beispiel die Anzahl der zu startenden Prozesse fest (5 für meinen Intel 980X). Jeder Prozess ist ein Python-Prozess (32bit) mit eigener Speicherzuweisung, genau wie bei der Verwendung von Spawn oder einem Unterprozess. Dann werden alle Jobs dem Pool der 5 Prozesse zugeführt und dieselben 5 Prozesse durchlaufen die Jobs, bis alle Jobs abgeschlossen sind.

Bei meinen Tests hatte ich keine Speicherprobleme. Wenn Sie "In_memory" verwenden, stellen Sie sicher, dass Sie keine wirklich großen Objekte (wie eine 1GB-Feature-Klasse) hineinladen, auch gibt es einige Einschränkungen, was "In_memory" verwenden kann und was nicht, aber ich benutze sie die ganze Zeit und sie kann die Verarbeitung wirklich beschleunigen, manchmal um den Faktor 4 schneller.

Der mp.pool "spawnt" auch neue Prozesse, aber ich konnte ihn nie richtig zum Laufen bringen, wollte immer alles in einem einzigen Prozess ausführen. Es würde den Pool starten, aber nur einen Prozess füttern. Das PP ist kostenlos und scheint gut zu funktionieren, also werde ich es verwenden.

Bitte besuchen Sie http://pythongisandstuff.wordpress.c. /stacyrendall/ für einige bekannte Probleme mit mp und PP steht es am Ende der Seite.

Es ist faszinierend, dass Multiprocessing für Sie nicht funktioniert. Können Sie bestätigen, dass kein Fehler vorliegt, sondern nur ein Prozess gleichzeitig ausgeführt wird? Ich benutze jetzt ausschließlich die Multiprocessing-Bibliothek, da PP anscheinend mit Erweiterungen nicht umgehen kann, aber noch nie Probleme damit hatte.

Ich weiß nicht viel über die Besonderheiten der Speichernutzung, aber ich habe ein paar Dinge gelernt (alle in Bezug auf die Verwendung der Multiprocessing-Bibliothek, aber sie können auch etwas Licht auf PP werfen). Die untergeordneten Prozesse nehmen einen Job aus dem Jobpool, schließen ihn ab, nehmen den nächsten verfügbaren Job und so weiter. Sie scheinen jedoch einen Teil (nicht den gesamten) des von ihnen verwendeten Speichers zu behalten, wenn Sie die Prozesse im Laufe der Zeit beobachten, wird der Speicherbedarf jedes einzelnen wachsen. Beim Ausführen einiger Modelle mit Network Analyst mit 4 untergeordneten Prozessen und einem Worker-Pool von anfänglich 56 Elementen erhielt ich einen Speicherfehler, nachdem der Gesamtspeicherbedarf aller vier Prozesse auf etwa 4 GB angewachsen war, etwa auf halbem Weg durch den Pool. Dies war bei weitem nicht die Grenzen des Computers, den ich benutzte. Meine beste Vermutung war, dass der gesamte Speicher immer noch im Besitz des Hauptprozesses war und somit seine 32-Bit-Grenze erreichte, aber die Zahlen summieren sich nicht wirklich auf, um diese Theorie zu stützen.

Der einfache Weg, dies zu umgehen, wird erst in Python 2.7 behoben, wo Sie einen maxtasksperchild-Wert festlegen können, obwohl dies nicht sehr gut zu funktionieren scheint - es erfordert komplizierteren Code, der entweder hängt oder ziemlich langsam läuft.

Der schwierige Weg besteht darin, den Pool alle n-mal für jedes Kind neu zu erstellen, je nachdem, wie groß die von den Arbeitern ausgeführten Operationen sind. Das ist natürlich nicht schön, funktioniert aber gut. Ich habe eine Reihe von Computern und eine Mischung aus Windows 7 und XP verwendet, während ich all dies getestet habe, und es scheint, dass auf einigen Computern die ersten parallelen Prozesse durch den Pool manchmal viel langsamer sind als die späteren, um mindestens 4 to 7x. Wenn dies auf einem Computer geschieht, sollten Sie n so hoch wie möglich halten, um die Anzahl Ihrer Aufgaben zu minimieren, die zuerst durch den Pool gehen. Wenn nicht, können Sie den Pool genauso gut neu erstellen, nachdem jeder parallele Prozess einmal ausgeführt wurde.

Lassen Sie es mich wissen, wenn Sie weitere Informationen wünschen, und ich werde versuchen, früher oder später ein Beispiel auf meinem Blog zu veröffentlichen!


Verarbeitung eines großen Raster-Datasets

Die zunehmende Auflösung von Raster-Datasets hat zu immer größeren Datenmengen geführt. Derzeit liegen die Datasets in der Größenordnung von Gigabyte und nehmen mit Milliarden von Rasterzellen zu. Während die Rechenleistung der Prozessoren und die Größe des Speichers in Computern merklich zugenommen haben, machen ältere Geräte und Algorithmen, die zum Manipulieren kleiner Raster mit gröberer Auflösung geeignet sind, die Verarbeitung dieser verbesserten Datenquellen teuer. [1]

Die Datenzerlegung, auch bekannt als Divide and Conquer, ist eine beliebte Strategie, die beim parallelen Computing verwendet wird und die wir nutzen werden, um ein großes Raster-Dataset parallel zu verarbeiten. Die in Rasteranalysetools verwendeten Algorithmen können grob in vier Kategorien eingeteilt werden – lokale, fokale, zonale und globale Operationen. Für einen tieferen Einblick in die Arten von zellenbasierten Raster-Operationen lesen Sie diesen Artikel. Lokale, fokale und zonale Rasteroperationen sind bei der Datenzerlegung einfach zu programmieren. Sobald die Daten entsprechend zerlegt sind, kann jeder Daten-„Chunk“ unabhängig von einem Prozess bearbeitet werden, ohne dass mit anderen Prozessen kommuniziert werden muss. Globale Rasteroperationen sind jedoch schwieriger in die Datenzerlegung zu integrieren und erfordern eine Kommunikation zwischen den Prozessen. Betrachten wir ein Beispiel für die Verarbeitung eines großen Raster-Datasets mit einer lokalen mathematischen Rasteroperation, Square Root. Die Local- oder Pro-Cell-Operationen lassen sich am einfachsten mit einer "Divide and Conquer"-Strategie parallelisieren, da der resultierende Wert in jeder Zelle nur vom Eingabewert an dieser Zellenposition abhängt. Für jede Zelle berechnet das Quadratwurzelwerkzeug die Quadratwurzel des Werts aus dieser Zelle. Die serielle Verwendung des Tools würde bedeuten, dass das Square Root-Tool einfach für den gesamten großen Datensatz ausgeführt wird, aber dieser Vorgang kann zeitaufwändig sein.

Standardmäßiger, nicht optimierter serieller Ansatz zur Verarbeitung eines großen Rasters

Stattdessen können wir durch die Datenzerlegung die Analyseaufgabe so umgestalten, dass mehrere Worker-Prozesse gleichzeitig verwendet werden, wodurch die Leistung der Gesamtanalyse verbessert wird. Die folgende Grafik zeigt das Aufteilen der Domäne eines großen Rasters in mehrere kleinere Blöcke und die Verwendung mehrerer Worker-Prozesse, um gleichzeitig eine Analyse für jeden der Unterabschnitte durchzuführen. Die Ergebnisse werden dann für die endgültige Ausgabe wieder zusammengefügt.

Große Raster parallel verarbeiten

Ein Beispielskript wird hier auf GitHub geteilt, das ausführlich erklärt, wie dieses Problem programmgesteuert mit ArcGIS Desktop und Python gelöst werden kann Multiprocessing Modul.


Multiprocessing mit Arcpy

Ich arbeite daran, durch eine große Anzahl von Datensätzen (900+) zu iterieren und für jeden viele speicherintensive Prozesse durchzuführen. Was zuvor eingerichtet wurde, um gdb-Feature-Classes zu durchlaufen, ist zu langsam und ich möchte mich auf Multiprocessing (mit shp) umstellen, um dieses Problem zu lösen.

Meine Frage ist, wenn ich ein eigenständiges Skript ausführe, das die Multiprocessing-Mod- und Pooling-Methoden enthält, läuft das Skript großartig und ich puffere 900+ Feature-Classes in 5 Minuten. Wenn ich diesen Teil in mein Hauptskript einfüge, scheint das Skript eine Zeitüberschreitung zu haben, obwohl es keine ausstehenden Speicherprobleme geben sollte (keine Ebenen, nichts im Speicher geschrieben).

Wäre es für mich besser, jeden Prozessschritt in eine separate .py-Datei zu schreiben und jeden Schritt zu importieren?

Ich bin mir nicht sicher, wo das schief geht, die Syntax ist identisch und es werden keine Fehler ausgegeben (der Prozess erzeugt nicht einmal etwas, wenn ich ihn in das Hauptskript integriere).

edit: Warum die Downvotes? Hätte ich noch etwas klarstellen sollen?


Verarbeitungszeit für Python Script mit Pythonwin vs. Script Tool

Ich frage mich, ob jemand einen dramatischen Unterschied in der Zeit erlebt hat, die zum Ausführen eines Python-Skripts als Skriptwerkzeug benötigt wird, im Vergleich zu direkt in Python oder Pythonwin IDE? Ich führe mein Skript in Pythonwin (Python 2.7.8, 32-Bit) aus und es dauert ungefähr 17 Minuten, aber wenn ich dasselbe Skript wie ein Skriptwerkzeug in ArcCatalog (Version 10.3.1) ausführe, verstrichene Zeit: 7 Stunden 39 Minuten 44 Sekunden . Das ist ein unglaublicher Unterschied und ich frage mich, woran es liegt. Dies ist in unserer Citrix-Umgebung, daher laufen die Programme nicht auf meinem Desktop-PC, sondern auf einem Citrix Blade. Was ich für ein Skript hielt, das eine praktikable Lösung bietet, ist nicht, wenn es bei der Verteilung in einer Toolbox um Größenordnungen länger dauert (wie den ganzen Tag).

Ich habe ein Skript, das Folgendes tut (ich kann den vollständigen Code posten, wenn das hilfreich sein könnte, obwohl es ziemlich lang ist und dies eine allgemeine Frage ist):

Zusammenfassung: Erstellt unidirektionale Repliken einer Datei-GDB von unserer Unternehmens-SDE-Instanz (Oracle) in unser Rechenzentrums-Dateisystem und überträgt sie dann mithilfe von Robocopy an unser Network Attached Storage-Gerät und sendet dann eine E-Mail der Protokolldatei.

Initiiert das Logging-Modul zur Logdatei

Überprüft SDE-Eingaben auf Replikationsanforderungen (Datasets sind versioniert und Feature-Classes/Tabellen enthalten GlobalIDs)

if schema == "S_R10_CNF" und str(child.isVersioned) == 'False':

login.info("Kind <0>ist nicht als versioniert registriert".format(child.Name))

login.info("Versuch sich zu registrieren.")

Logging.info("Registriert <0>als Versioniert erfolgreich".format(child.Name))

Logging.warning("Kann sich nicht als versioniert registrieren wegen Ausnahme: <0>".format(e.message))

login.warning(" <0>ist NICHT als versioniert registriert und wird NICHT repliziert".format(child.Name))

aus Sammlungen importieren defaultdict

für fc in arcpy.ListFeatureClasses("","",ds):

für tb in arcpy.ListTables('S_R10_CNF.*'):

print(' <0> fehlt GLOBALID in Tabelle <1>'.format(tb))

login.info(" <0>fehlt GlobalIDs! Es wird versucht, sie hinzuzufügen. ".format(i))

Logging.info("GlobalIDs zum Datensatz hinzugefügt: <0>".format(i))

logging.warn("GlobalIDs können nicht zum Datensatz <0> aufgrund von Ausnahme: <1> hinzugefügt werden".format(i, e.message))

Logging.warn("Folgende haben keine GlobalIDs und werden nicht repliziert: <0>".format(i))

Erstellt die Ausgabedatei GDB, falls sie noch nicht existiert

wenn nicht arcpy.Exists(gdbName):

Logging.info("Erstellte untergeordnete Ziel-GDB " + gdbName)

Generiert einen Namen für das Replikat basierend auf dem Dataset-Namen

ReplikName = ReplikBase + "_NAS1Way"

truncLen = 32 - len("_NAS1Way")

if len(replicaName) > 32: #Replikatname darf nur 32 Zeichen oder weniger lang sein oder das Replikat schlägt mit einer vagen Fehlermeldung fehl

Überprüft, ob das Replikat bereits vorhanden ist, wenn ja, synchronisiert und kopiert es Metadaten, wenn nicht, erstellt es das Replikat

# Überprüfen Sie die Namen der Replikate in Eltern und Kind

ReplikList = [x.name.partition(".")[2] für x in arcpy.da.ListReplicas()]

ReplikListGDB = [x.name für x in arcpy.da.ListReplicas(outGDB)]

if replicaName in replicaList:

if replicaName in replicaListGDB:

arcpy.SynchronizeChanges_management(outGDB, "DBO."+replicaName, arcpy.env.workspace, "FROM_GEODATABASE2_TO_1")

Logging.info("Synchronisiertes Einweg-Replikat für: <1>".format(replicaName, DS))

login.warn("Replikatname <0>in übergeordneter SDE gefunden, aber nicht in untergeordneter GDB".format(replicaName))

Logging.warn(" Replica <0> unregistrieren, untergeordnete Daten löschen und erneut ausführen!".format(replicaName))

Logging.error("Replikat <0> kann nicht synchronisiert werden wegen Ausnahme: <1>".format(replicaName,e.message))

status='Metadaten für <0> werden synchronisiert'.format(DS)

Logging.error("Metadaten <0> können nicht synchronisiert werden wegen Ausnahme: <1>".format(replicaName,e.message))

status='Erstellen einer 1-Wege-Replik für <0>'.format(DS)

arcpy.CreateReplica_management(featureList, "ONE_WAY_REPLICA", outGDB, ReplikName, "FULL", "", "ALL_ROWS", "", "GET_RELATED")

Logging.info("One Way Replica erstellt für: <0>".format(DS))

Logging.info(" Replikat heißt: <0>".format(replicaName))

Logging.error("Replik für Datensatz <0> kann nicht erstellt werden wegen Ausnahme: <1>".format(DS,e.message))

Ruft Robocopy über das Unterprozessmodul auf, um die Datei GDB auf das NAS-Laufwerk zu kopieren

subprocess.call(r'NET USE Z: /del')

subprocess.call(r'NET USE Z: <0>/user:<username> pwd'.format(destPath))

status='Replikat-GDB-Änderungen auf NAS-Laufwerk übertragen '.format(destPath)

subprocess.call(["robocopy", gdbPath, "Z:", "*.*", "/e", "/z", "/dcopy:T", "/purge", "/xo", " /xf", "*.lock", "<0>".format(os.path.join(gdbPath, "robocopy_log.txt")), "/fft", "/log:<0>".format( os.path.join(gdbPath, "robocopy_log.txt")), "/tee"])

Sendet eine E-Mail der Protokolldatei

mailserver.sendmail(fromEmail, toEmailList, msg)

Die langsamsten Teile des Skripts scheinen arcpy.SynchronizeChanges_management auszuführen, wenn es sich in einem Toolbox-Skriptwerkzeug befindet, selbst wenn KEINE Änderungen zu synchronisieren sind! Das Kontrollkästchen ist aktiviert, um Python-Skript in Prozess ausführen zu lassen.

Ich untersuche die Verwendung von Multiprocessing, da es 16 Kerne auf dem Citrix-Server gibt, um einige der Prüfungen auszuführen und Replikate zu erstellen, um die Dinge zu beschleunigen. Hat jemand eine Ahnung, warum das gleiche Skript als Skriptwerkzeug lächerlich länger dauert als direkt in Python ohne ArcGIS-Overhead?


1.5.1.1 Skript in ein Werkzeug umwandeln

Lassen Sie uns nun dieses Skript in ArcGIS Pro in ein Skriptwerkzeug konvertieren, um uns mit dem Prozess vertraut zu machen, und wir untersuchen die Unterschiede zwischen ArcGIS Desktop und ArcGIS Pro beim Arbeiten mit Skriptwerkzeugen (Hinweis: Es gibt keine anderen als die Benutzeroberfläche sieht etwas anders aus).

Wir beginnen mit dem Öffnen von ArcGIS Pro. Sie werden aufgefordert, sich anzumelden (verwenden Sie Ihr Penn State ArcGIS Online-Konto, das Sie bereits haben sollten) und ein Projekt zu erstellen, wenn Pro gestartet wird.

Die Anmeldung bei ArcGIS Pro ist im Vergleich zu Desktop eine wichtige neue Entwicklung für die Ausführung von Code in Pro. Wie Sie vielleicht wissen, arbeitet Pro mit einer anderen Lizenzstruktur, sodass es regelmäßig mit den Lizenzservern von Esri "nach Hause telefoniert", um zu überprüfen, ob Sie über eine gültige Lizenz verfügen. Nachdem Sie Desktop installiert und Ihre Lizenz eingerichtet hatten, konnten Sie es ohne Probleme für die 12 Monate, in denen die Lizenz gültig war, online oder offline ausführen. Da Pro regelmäßig bei Esri eincheckt, müssen wir bedenken, dass wir überprüfen sollten, ob Pro noch angemeldet ist, wenn unser Code aufgrund eines nicht lizenzierten Fehlers einer Erweiterung oder aufgrund eines allgemeineren Lizenzproblems nicht mehr funktioniert an alle, dies ist kein Problem, da Sie Pro normalerweise auf einem mit dem Internet verbundenen Computer verwenden und die Lizenzprüfungen nicht bemerken. Wenn Sie Ihren Computer für längere Zeit offline schalten, müssen Sie die Offline-Lizenzierungsoptionen von Esri prüfen.

Projekte sind Pros Weg, all Ihre Karten, Layouts, Aufgaben, Daten, Toolboxen usw. zu organisieren. Wenn Sie von Desktop kommen, stellen Sie es sich als MXD mit einigen weiteren Funktionen vor (z. B. das Zulassen mehrerer Layouts für Ihre Karten).

Wählen Sie Erstellen eines neuen Projekts mit der leeren Vorlage, geben Sie ihm einen aussagekräftigen Namen und legen Sie es in einem Ordner ab, der für Ihren lokalen Computer geeignet ist.

Anschließend wird Pro mit Ihrer bereits erstellten Toolbox ausgeführt. In der Abbildung unten habe ich auf die Toolboxes geklickt, um sie zu erweitern, um die Toolbox anzuzeigen, die denselben Namen wie mein Projekt hat.

Wenn wir mit der rechten Maustaste auf unsere Toolbox klicken, können wir ein neues >-Skript erstellen.

Es öffnet sich ein Fenster, in dem wir einen Namen für unser Skript („Lesson1A“) und eine Bezeichnung für unser Skript („Geog 489 Lektion 1A“) eingeben können, und dann verwenden wir das Symbol zum Durchsuchen der Datei, um die Skriptdatei zu finden, die wir früher gespeichert. Wenn Ihr Skript nicht in diesem Ordner angezeigt wird oder Sie die Meldung „Container is empty“ erhalten, drücken Sie F5 auf Ihrer Tastatur, um die Ansicht zu aktualisieren.

Wir werden uns (noch) nicht dafür entscheiden, "Skript zu importieren", irgendwelche Parameter zu definieren oder (noch) die Validierung zu untersuchen. Wenn wir auf OK klicken, haben wir unser Skript-Tool in Pro erstellt. Wir werden unser Skript-Tool (noch) nicht ausführen, da es derzeit erwartet, die Foxlake-DEM-Daten in C:dataelevation zu finden und die Ergebnisse in diesen Ordner zurückzuschreiben, was nicht sehr praktisch ist. Es hat auch den hartcodierten Cutoff von 3500 in den Code eingebettet. Sie können das FoxLake-DEM hier herunterladen.

Um das Skript benutzerfreundlicher zu gestalten, werden wir einige Änderungen vornehmen, damit wir den Speicherort der Eingabe- und Ausgabedateien auswählen und dem Benutzer die Eingabe des Cutoff-Werts ermöglichen können. Später werden wir auch die Validierung verwenden, um zu überprüfen, ob dieser Cutoff-Wert innerhalb des im Raster vorhandenen Wertebereichs liegt, und wenn nicht, ändern wir ihn.

Wir können unser Skript in Pro bearbeiten, aber wenn wir dies tun, wird es in Notepad geöffnet, was nicht die beste Umgebung zum Codieren ist. Sie können Notepad verwenden, wenn Sie möchten, aber ich würde vorschlagen, das Skript erneut in Ihrem bevorzugten Texteditor (ich mag Notepad ++) zu öffnen oder einfach Spyder zu verwenden.

Wenn Sie möchten, können Sie diesen bevorzugten Editor ändern, indem Sie die Geoverarbeitungsoptionen von Pro ändern (siehe http://pro.arcgis.com/en/pro-app/help/analysis/geoprocessing/basics/geoprocessing-options.htm). Um auf diese Optionen in Pro zuzugreifen, klicken Sie auf Home -> Optionen -> Geoverarbeitungsoptionen. Hier können Sie auch eine Option auswählen, um Tools und Skripte automatisch auf Pro-Kompatibilität zu validieren (damit Sie die Analyze Tools for Pro nicht jedes Mal manuell ausführen müssen).

Wir werden jetzt ein paar Änderungen an unserem Code vornehmen, indem wir die hartcodierten Pfade in den Zeilen 8 und 17 und den hartcodierten cutoffElevation-Wert in Zeile 9 austauschen. Außerdem richten wir in Zeile 10 eine outPath-Variable ein und setzen sie auf arcpy .env.Arbeitsbereich.

Sie erinnern sich vielleicht an GEOG 485 oder Ihre anderen Erfahrungen mit Desktop, dass der Standardarbeitsbereich in Desktop normalerweise default.gdb in Ihrem Benutzerpfad ist. Pro ist intelligenter und legt den Standard-Workspace als Geodatabase Ihres Projekts fest. Wir nutzen dies, um unser Ausgabe-Raster in unseren Projektarbeitsbereich einzufügen. Beachten Sie den Unterschied in der Art des Parameters, den wir in den Zeilen 8 und 9 verwenden. Es ist in Ordnung, den Pfad als Text zu erhalten, aber wir möchten die Zahl in cutoffElevation nicht als Text erhalten, da wir ihn als a . benötigen Nummer.

Um die Programmierung zu vereinfachen, werden wir in Pro einen anderen Parametertyp angeben und diesen an unser Skript übergeben. Um dies zu erreichen, verwenden wir GetParameter anstelle von GetParameterAsText.

Sobald Sie diese Änderungen vorgenommen haben, speichern Sie die Datei und wir kehren zu unserem Skript-Tool in Pro zurück und aktualisieren es, um die gerade definierten Parameter zu verwenden. Klicken Sie mit der rechten Maustaste auf das Skriptwerkzeug in der Toolbox, wählen Sie Eigenschaften und klicken Sie dann auf Parameter. The first parameter we defined (remember Python counts from 0) was the path to our input raster (inRaster), so let's set that up. Click in the text box under Label and type “Input Raster” and when you click into Name you’ll see that Name is already automatically populated for you. Next, click the Data Type (currently String) and change it to “Raster Dataset” and we’ll leave the other values with their defaults.

Click the next Label text box below your first parameter (currently numbered with a *) and type “Cutoff Value” and change the Data Type to Long (which is a type of number) and we’ll keep the rest of the defaults here too. The final version should look as in the figure below.

Click OK and then we’ll run the tool to test the changes we made by double-clicking it. Use the file icon alongside our Input Raster parameter to navigate to your foxlake raster (which is the FoxLake digital elevation model (DEM) in your Lesson 1 data folder) and then enter 3500 into the cutoff value parameter and click OK to run the tool.

The tool should have executed without errors and placed a raster called foxlake_hi_10 into your project geodatabase.


1.5.1 Making a Script Tool

Here’s another simple script that finds all cells over 3500 meters in an elevation raster and makes a new raster that codes all those cells as 1. Remaining values in the new raster are coded as 0. By now, you’re probably familiar with this type of “map algebra” operation which is common in site selection and other GIS scenarios.

Just in case you’ve forgotten, the expression Raster(inRaster) tells arcpy that it needs to treat your inRaster variable as a raster dataset so that you can perform map algebra on it. If you didn't do this, the script would treat inRaster as just a literal string of characters (the path) instead of a raster dataset.

You can probably easily work out what this script is doing but, just in case, the main points to remember on this script are:

  • Notice the lines of code that check out the Spatial Analyst extension before doing any map algebra and check it back in after finishing. Because each line of code takes some time to run, avoid putting unnecessary code between checkout and checkin. This allows others in your organization to use the extension if licenses are limited. The extension automatically gets checked back in when your script ends, thus some of the Esri code examples you will see do not check it in. However, it is a good practice to explicitly check it in, just in case you have some long code that needs to execute afterward, or in case your script crashes and against your intentions "hangs onto" the license.
  • inRaster begins as a string, but is then used to create a Raster object once you run Raster(inRaster). A Raster object is a special object used for working with raster datasets in ArcGIS. It's not available in just any Python script: you can use it only if you import the arcpy module at the top of your script.
  • cutoffElevation is a number variable that you declare early in your script and then use later on when you build the map algebra expression for your outRaster.
  • Der Ausdruck outRaster = Raster(inRaster) > cutoffElevation is saying, in plain terms, "Make a new raster and call it outRaster. Do this by taking all the cells of the raster dataset at the path of inRaster that are greater than the number assigned to the variable cutoffElevation."
  • outRaster is also a Raster object, but you have to call the method outRaster.save() in order to make it permanent on disk. The save() method takes one argument, which is the path to which you want to save.

Copy the code above into a file called Lesson1A.py (or similar as long as it has a .py extension) in spyder or your favorite IDE or text editor and then save it.

We don’t need to do anything to this code to get it to work in Python 3, it will be fine just as it is. Feel free to check it against Analyze Tools for Pro if you like. Your results should say “Analyze Tools for Pro Completed Successfully” with the lack of warnings signifying that the code you supplied is compatible with Python 3.


4 Antworten 4

Edit, this workaround is no longer needed as of 2.91.

This looks like a kind of ms-windows specific problem. at least a conflict with having an embedded Python interpreter and the environment which multiprocessing expects. (its not exactly a bug, in that its not like Blender devs can fix some mistake to get this working).

Currently multiprocessing makes the assumption that its running in Python und nicht running inside an application.

I managed to get multi-processing working on ms-windows, doing some workarounds.

  • sys.executable needs to point to Python executable.
  • The scripts __file__ needs to point to a file on-disk
    (not always the case - when executing a text block for example).

Heres an example of a workaround:

Blender+Python can do this, but nicht using multiprocessing . $endgroup$ &ndash ideasman42 Jun 22 '15 at 22:22

I think this statement in the same docs says a lot:

"So far, no work has gone into making Blender’s python integration thread safe, so until its properly supported, best not make use of this. "

The statement that subprocess and multiprocess can be used with Blender isn't false, just may be interpreted in reverse with regards to multiprocess module.

  • subprocess can call external apps fine with .call or .Popen , non-blocking or blocking. Works as expected, threaded or non threaded.
  • multiprocess probably doesn't work (right now) as you might expect, how I interpreted this statement originally was, "you can call several instances of Blender from a python program using multiprocessing , that way you have control over what ends when and can use multiple CPU's.

In the end the warning is reasonable clear (I think?) that we aren't to expect threaded execution of calls to Blender functions within the one Blender executable. (for instance function calls that take a lot of time, like baking.. it is possible to write scripts to do all that and finally when all blender processes have ended , merge the result in an additional script. The work (scripting wise) may be a little bit more, but that seems to be where it's at at the moment.

Ubuntu 14.04
On Ubuntu that code leaves headless processes behind, which only finish when the main Blender executable is terminated. If there's a problem it's not entirely localized to windows.


Geoprocessing services can have a result map service to create a digital map image of task results. Digital maps contain visual representations of geographic datasets that communicate information. Digital maps are transported across the Web as images (such as a .jpeg ). A map image, byte for byte, contains far more human-interpretable information than raw features in a feature class. Map images are also manageable—they are easily compressed, they can be tiled into manageable chunks, and there are established methods for transporting and viewing them across the Web.

Map images are created by an ArcGIS for Server map service and are the result of publishing an ArcMap document ( .mxd ). Because of the characteristics of a map image, you may want to create one for the results of your geoprocessing task and transport the image across the Web rather than transporting the result dataset or datasets. Geoprocessing services can have a result map service used by ArcGIS for Server to create map images of your output data.


Syntax

The input feature classes or feature datasets whose coordinates are to be converted.

The location of each new output feature class or feature dataset.

The coordinate system to be used to project the inputs.

Valid values are a Spatial Reference object, a file with a .prj extension, or a string representation of a coordinate system.

The feature class or the feature dataset used to specify the output coordinate system used for projection.

Name of the geographic transformation to be applied to convert data between two geographic coordinate systems (datums).


Schau das Video: Python Multiprocessing Tutorial: Run Code in Parallel Using the Multiprocessing Module (Oktober 2021).