Comment les performances du curseur d'accès aux données sont-elles améliorées par rapport aux versions précédentes?

18

Le module d'accès aux données a été introduit avec ArcGIS version 10.1. ESRI décrit le module d'accès aux données comme suit ( source ):

Le module d'accès aux données, arcpy.da, est un module Python pour travailler avec des données. Il permet de contrôler la session d'édition, les opérations d'édition, une prise en charge améliorée du curseur (y compris des performances plus rapides), des fonctions de conversion des tables et des classes d'entités vers et depuis les tableaux NumPy, et la prise en charge des flux de travail de gestion des versions, des répliques, des domaines et des sous-types.

Cependant, il existe très peu d'informations sur la raison pour laquelle les performances du curseur sont améliorées par rapport à la génération précédente de curseurs.

La figure ci-jointe montre les résultats d'un test de référence sur la nouvelle daméthode UpdateCursor par rapport à l'ancienne méthode UpdateCursor. Essentiellement, le script effectue le workflow suivant:

  1. Créer des points aléatoires (10, 100, 1000, 10000, 100000)
  2. Échantillonner au hasard à partir d'une distribution normale et ajouter de la valeur à une nouvelle colonne dans la table attributaire de points aléatoires avec un curseur
  3. Exécutez 5 itérations de chaque scénario ponctuel aléatoire pour les nouvelles et anciennes méthodes UpdateCursor et écrivez la valeur moyenne dans les listes
  4. Tracer les résultats

Que se passe-t-il en arrière-plan avec le dacurseur de mise à jour pour améliorer les performances du curseur au degré indiqué sur la figure?


entrez la description de l'image ici


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
la source

Réponses:

25

L'un des développeurs d' arcpy.daici. Nous avons obtenu la performance là où elle est parce que la performance était notre principale préoccupation : le principal reproche avec les anciens curseurs était qu'ils étaient lents, pas qu'ils manquaient de fonctionnalités particulières. Le code utilise les mêmes ArcObjects sous-jacents disponibles dans ArcGIS depuis 8.x (l'implémentation CPython du curseur de recherche, par exemple, ressemble beaucoup à des exemples de code comme celui-ci dans son implémentation sauf, vous savez, en C ++ au lieu de C #).

Les deux principales choses que nous avons faites pour obtenir l'accélération sont donc:

  1. Éliminez les couches d'abstraction: l'implémentation initiale du curseur Python était basée sur l'ancien objet GPDispatch basé sur Dispatch / COM , ce qui permettait d'utiliser la même API dans n'importe quel langage pouvant consommer des objets COM Dispatch . Cela signifie que vous disposiez d'une API qui n'était pas particulièrement bien optimisée pour un environnement unique, mais cela signifiait également qu'il y avait beaucoup de couches d'abstraction pour les objets COM pour publier et résoudre des méthodes au moment de l'exécution, par exemple. Si vous vous en souvenez avant ArcGIS 9.3, il était possible d'écrire des scripts de géotraitement en utilisant la même interface maladroite de nombreux langages, même Perl et Ruby . La paperasse supplémentaire qu'un objet doit faire pour gérer leIDispatch les choses ajoutent beaucoup de complexité et de ralentissement aux appels de fonction.
  2. Créez une bibliothèque C ++ étroitement intégrée et spécifique à Python en utilisant des idiomes et des structures de données Pythonic: l'idée d' un Rowobjet et la while cursor.Next():danse vraiment étrange étaient tout simplement inefficaces en Python. La récupération d'un élément à partir d'une liste est une opération très rapide et se réduit à quelques appels de fonction CPython (essentiellement un __getitem__appel, fortement optimisé sur les listes). Faire row.getValue("column")par comparaison est plus lourd: il effectue une __getattr__extraction de la méthode (sur laquelle il doit créer un nouvel objet de méthode liée), puis appelle cette méthode avec les arguments donnés ( __call__). Chaque partie de l' arcpy.daimplémentation est très étroitement intégrée à l'API CPython avec beaucoup de C ++ ajusté à la main pour le rendre rapide, en utilisant des structures de données Python natives (et une intégration numpy, aussi, pour encore plus de vitesse et d'efficacité de la mémoire).

Vous remarquerez également que dans presque tous les benchmarks ( voir ces diapositives par exemple ), les objets arc dans .Net et C ++ sont toujours deux fois plus rapides que arcpy.dadans la plupart des tâches. L'utilisation de code Python arcpy.daest plus rapide, mais toujours pas plus rapide qu'un langage compilé de niveau inférieur.

TL; DR : daest plus rapide car il daest implémenté dans des Arcobjects / C ++ / CPython simples et purs qui ont été spécifiquement conçus pour générer un code Python rapide.

Jason Scheirer
la source
4

Liés à la performance

  • Le curseur parcourt uniquement la liste des champs par défaut (pas la base de données entière)

Autres pas directement liés aux performances, mais belles améliorations:

  • Possibilité d'utiliser des jetons (par exemple, SHAPE @ LENGTH, SHAPE @ XY) pour accéder à la géométrie des entités
  • Capacité à parcourir les bases de données (en utilisant la méthode arcpy.da.Walk )
oeuvre21
la source