Wie ist die Leistung des Datenzugriffs-Cursors im Vergleich zu früheren Versionen verbessert?

18

Das Datenzugriffsmodul wurde mit ArcGIS Version 10.1 eingeführt. ESRI beschreibt das Datenzugriffsmodul wie folgt ( Quelle ):

Das Datenzugriffsmodul arcpy.da ist ein Python-Modul zum Arbeiten mit Daten. Es ermöglicht die Steuerung der Editiersitzung, des Editiervorgangs, eine verbesserte Cursorunterstützung (einschließlich einer schnelleren Leistung), Funktionen zum Konvertieren von Tabellen und Feature-Classes in und aus NumPy-Arrays sowie die Unterstützung von Versionierungs-, Replikations-, Domänen- und Subtyp-Workflows.

Es gibt jedoch nur sehr wenige Informationen darüber, warum die Cursorleistung gegenüber der vorherigen Cursorgeneration so verbessert wurde.

Die beigefügte Abbildung zeigt die Ergebnisse eines Benchmark-Tests für die neue daMethode UpdateCursor im Vergleich zur alten UpdateCursor-Methode. Im Wesentlichen führt das Skript den folgenden Workflow aus:

  1. Erstelle zufällige Punkte (10, 100, 1000, 10000, 100000)
  2. Wählen Sie eine zufällige Stichprobe aus einer Normalverteilung und fügen Sie mit einem Cursor einen Wert zu einer neuen Spalte in der Attributtabelle für zufällige Punkte hinzu
  3. Führen Sie 5 Iterationen jedes Zufallspunktszenarios für die neue und die alte UpdateCursor-Methode aus und schreiben Sie den Mittelwert in Listen
  4. Zeichnen Sie die Ergebnisse

Was passiert hinter den Kulissen mit dem daAktualisierungscursor, um die Cursorleistung in dem in der Abbildung gezeigten Maße zu verbessern?


Bildbeschreibung hier eingeben


import arcpy, os, numpy, time
arcpy.env.overwriteOutput = True

outws = r'C:\temp'
fc = os.path.join(outws, 'randomPoints.shp')

iterations = [10, 100, 1000, 10000, 100000]
old = []
new = []

meanOld = []
meanNew = []

for x in iterations:
    arcpy.CreateRandomPoints_management(outws, 'randomPoints', '', '', x)
    arcpy.AddField_management(fc, 'randFloat', 'FLOAT')

    for y in range(5):

        # Old method ArcGIS 10.0 and earlier
        start = time.clock()

        rows = arcpy.UpdateCursor(fc)

        for row in rows:
            # generate random float from normal distribution
            s = float(numpy.random.normal(100, 10, 1))
            row.randFloat = s
            rows.updateRow(row)

        del row, rows

        end = time.clock()
        total = end - start
        old.append(total)

        del start, end, total

        # New method 10.1 and later
        start = time.clock()

        with arcpy.da.UpdateCursor(fc, ['randFloat']) as cursor:
            for row in cursor:
                # generate random float from normal distribution
                s = float(numpy.random.normal(100, 10, 1))
                row[0] = s
                cursor.updateRow(row)

        end = time.clock()
        total = end - start
        new.append(total)
        del start, end, total
    meanOld.append(round(numpy.mean(old),4))
    meanNew.append(round(numpy.mean(new),4))

#######################
# plot the results

import matplotlib.pyplot as plt
plt.plot(iterations, meanNew, label = 'New (da)')
plt.plot(iterations, meanOld, label = 'Old')
plt.title('arcpy.da.UpdateCursor -vs- arcpy.UpdateCursor')
plt.xlabel('Random Points')
plt.ylabel('Time (minutes)')
plt.legend(loc = 2)
plt.show()
Aaron
quelle

Antworten:

25

Einer der Entwickler von arcpy.dahier. Wir haben die Leistung genau dort erhalten, wo sie ist, weil die Leistung unser Hauptanliegen war : Die Hauptprobleme bei den alten Cursorn waren, dass sie langsam waren, und nicht, dass ihnen eine bestimmte Funktionalität fehlte. Der Code verwendet dieselben zugrunde liegenden ArcObjects, die in ArcGIS seit 8.x verfügbar sind (die CPython-Implementierung des Suchcursors sieht beispielsweise in ihrer Implementierung ähnlich aus, mit Ausnahme von C ++ anstelle von C #).

Die beiden wichtigsten Dinge, die wir getan haben, um die Beschleunigung zu erreichen, sind:

  1. Beseitigen von Abstraktionsebenen: Die anfängliche Implementierung des Python-Cursors basierte auf dem alten Dispatch / COM-basierten GPDispatch-Objekt. Dadurch konnte dieselbe API in jeder Sprache verwendet werden, in der COM Dispatch-Objekte verwendet werden konnten . Dies bedeutet, dass Sie eine API hatten, die nicht für jede einzelne Umgebung besonders gut optimiert war, aber es bedeutete auch, dass die COM-Objekte viele Abstraktionsebenen aufwiesen, um beispielsweise zur Laufzeit Methoden anzukündigen und aufzulösen. Wenn Sie sich vor ArcGIS 9.3 erinnern, war es möglich, Geoverarbeitungsskripten mit derselben klobigen Oberfläche in vielen Sprachen zu schreiben, sogar in Perl und Ruby . Der zusätzliche Papierkram, den ein Objekt erledigen muss, um das Problem zu lösenIDispatch Zeug fügt eine Menge Komplexität und Verlangsamung zu Funktionsaufrufen hinzu.
  2. Erstellen Sie eine eng integrierte, Python-spezifische C ++ - Bibliothek mit Pythonic-Idiomen und -Datenstrukturen: Die Idee eines RowObjekts und der wirklich seltsame while cursor.Next():Tanz waren in Python einfach ineffizient. Das Abrufen eines Elements aus einer Liste ist ein sehr schneller Vorgang und vereinfacht sich bis auf ein paar CPython-Funktionsaufrufe (im Grunde genommen ein __getitem__stark auf Listen optimierter Aufruf). Im row.getValue("column")Vergleich ist es schwerer, eine __getattr__Methode abzurufen (für die ein neues gebundenes Methodenobjekt erstellt werden muss), und dann diese Methode mit den angegebenen Argumenten aufzurufen ( __call__). Jeder Teil der arcpy.daImplementierung ist sehr eng in die CPython-API integriert mit viel handoptimiertem C ++, um es schnell zu machen, mit nativen Python-Datenstrukturen (und auch Numpy-Integration für noch mehr Geschwindigkeit und Speichereffizienz).

Sie werden auch feststellen, dass arcobjects in .Net und C ++ in fast jedem Benchmark ( siehe diese Folien zum Beispiel ) immer noch doppelt so schnell sind wie arcpy.dain den meisten Aufgaben. Die Verwendung von Python-Code arcpy.daist schneller, aber immer noch nicht schneller als eine kompilierte Sprache niedrigerer Ebene.

TL; DR : daist schneller, weil daes in direktem, unverfälschtem Arcobjects / C ++ / CPython implementiert ist, das speziell für den schnellen Python-Code entwickelt wurde.

Jason Scheirer
quelle
4

Leistungsbezogen

  • Der Cursor durchläuft standardmäßig nur die festgelegte Liste der Felder (nicht die gesamte Datenbank)

Andere, die nicht direkt mit der Leistung zu tun haben, aber nette Verbesserungen:

  • Möglichkeit, Token (z. B. SHAPE @ LENGTH, SHAPE @ XY) für den Zugriff auf Feature-Geometrie zu verwenden
  • Durchsuchen von Datenbanken (mit der Methode arcpy.da.Walk )
artwork21
quelle