¿Cómo se mejora el rendimiento del cursor de acceso a los datos en comparación con las versiones anteriores?

18

El módulo de acceso a datos se introdujo con ArcGIS versión 10.1. ESRI describe el módulo de acceso a datos de la siguiente manera ( fuente ):

  

El módulo de acceso a datos, arcpy.da, es un módulo de Python para trabajar con   datos. Permite el control de la sesión de edición, operación de edición, mejorado.   soporte del cursor (incluyendo un rendimiento más rápido), funciones para   convertir tablas y clases de entidad desde y hacia arreglos NumPy, y   soporte para la creación de versiones, réplicas, dominios y subtipos de flujos de trabajo.

Sin embargo, hay muy poca información sobre por qué el rendimiento del cursor se ha mejorado tanto en comparación con la generación anterior de cursores.

La figura adjunta muestra los resultados de una prueba de referencia en el nuevo método UpdateCursor da versus el método UpdateCursor anterior. Esencialmente, el script realiza el siguiente flujo de trabajo:

  1. Crear puntos aleatorios (10, 100, 1000, 10000, 100000)
  2. Muestra aleatoriamente de una distribución normal y agrega valor a una nueva columna en la tabla de atributos de puntos aleatorios con un cursor
  3. Ejecute 5 iteraciones de cada escenario de puntos aleatorios para los métodos UpdateCursor nuevos y antiguos y escriba el valor medio en las listas
  4. traza los resultados

¿Qué sucede detrás de escena con el cursor de actualización da para mejorar el rendimiento del cursor hasta el grado que se muestra en la figura?

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()
    
pregunta Aaron 29.07.2014 - 21:21

2 respuestas

24

Uno de los desarrolladores de arcpy.da aquí. Obtuvimos el rendimiento donde está porque el rendimiento era nuestra principal preocupación : la principal queja con los cursores anteriores era que eran lentos, no que carecían de ninguna funcionalidad en particular. El código utiliza el mismo ArcObjects subyacente disponible en ArcGIS desde la versión 8.x (la implementación CPython del cursor de búsqueda, por ejemplo, se parece mucho a codifique ejemplos como este en su implementación excepto, ya sabe, en C ++ en lugar de C #).

Las dos cosas principales que hicimos para lograr la aceleración son:

  1. Eliminar capas de abstracción: la implementación inicial del cursor de Python se basó en el antiguo Dispatch / COM based GPDispatch object , que le permitió a uno usar la misma API en cualquier idioma que pudiera consumir COM Dispatch objetos . Esto significa que tenía una API que no estaba particularmente bien optimizada para ningún entorno individual, pero también significaba que había un lote de capas de abstracción para que los objetos COM hicieran publicidad y resolvieran métodos en tiempo de ejecución , por ejemplo. Si recuerda que antes de ArcGIS 9.3, era posible escribir scripts de geoprocesamiento usando la misma interfaz torpe en muchos idiomas, incluso Perl y Ruby . El papeleo adicional que necesita hacer un objeto para manejar las cosas IDispatch agrega mucha complejidad y desaceleración a las llamadas de función.
  2. Cree una biblioteca de C ++ específica de Python estrechamente integrada utilizando expresiones idiomáticas y estructuras de datos: la idea de a Row object y el realmente extraño while cursor.Next(): dance eran simplemente ineficientes en Python. Obtener un elemento de una lista es una operación muy rápida y se simplifica a solo un par de llamadas de función CPython (básicamente una llamada __getitem__ , muy optimizada en las listas). Hacer row.getValue("column") en comparación es más pesado: hace un __getattr__ para obtener el método (en el que necesita crear un nuevo objeto de método vinculado), luego llame a ese método con los argumentos dados ( __call__ ). Cada parte de la implementación arcpy.da es muy estrechamente integrada con la API de CPython con una gran cantidad de C ++ sintonizado a mano para hacerlo más rápido, utilizando estructuras de datos nativas de Python (y también integración numpy, para incluso más velocidad y eficiencia de memoria).

También notará que en casi cualquier punto de referencia ( vea estas diapositivas por ejemplo ) , los objetos de arco en .Net y C ++ siguen siendo dos veces más rápidos que arcpy.da en la mayoría de las tareas. El código Python que usa arcpy.da es más rápido, pero aún no es más rápido que un lenguaje compilado de nivel inferior.

TL; DR : da es más rápido porque da se implementa en Arcobjects / C ++ / CPython directo y sin adulterar, que fue diseñado específicamente para dar como resultado un código Python rápido.

    
respondido por el Jason Scheirer 29.07.2014 - 22:01
4

Relacionado con el rendimiento

  • El cursor solo se repite en la lista de campos establecida de forma predeterminada (no en toda la base de datos)

Otros no relacionados directamente con el rendimiento, pero con mejoras agradables:

  • Capacidad para usar tokens (por ejemplo, SHAPE @ LENGTH, SHAPE @ XY) para acceder a la geometría de la función
  • Capacidad para recorrer las bases de datos (utilizando arcpy.da.Walk método)
respondido por el artwork21 29.07.2014 - 21:40

Lea otras preguntas en las etiquetas