¿Cómo se mejora el rendimiento del cursor de acceso a 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, la operación de edición, la compatibilidad mejorada con el cursor (incluido un rendimiento más rápido), las funciones para convertir tablas y clases de entidad hacia y desde matrices NumPy, y la compatibilidad con versiones, réplicas, dominios y flujos de trabajo de subtipos.

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

La figura adjunta muestra los resultados de una prueba de referencia en el nuevo damétodo UpdateCursor versus el antiguo método UpdateCursor. 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 punto aleatorio para los métodos UpdateCursor nuevos y antiguos y escriba el valor medio en las listas
  4. Graficar los resultados

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


ingrese la descripción de la imagen aquí


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
fuente

Respuestas:

25

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

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

  1. Elimine las capas de abstracción: la implementación inicial del cursor de Python se basó en el antiguo objeto GPDispatch basado en Dispatch / COM , que le permitió a uno usar la misma API en cualquier lenguaje que pudiera consumir objetos COM Dispatch . Esto significa que tenía una API que no estaba particularmente bien optimizada para un entorno único, pero también significaba que había muchas capas de abstracción para que los objetos COM publicitaran y resolvieran métodos en tiempo de ejecución, por ejemplo. Si recuerda antes de ArcGIS 9.3, era posible escribir scripts de geoprocesamiento utilizando esa misma interfaz torpe en muchos idiomas, incluso Perl y Ruby . El papeleo adicional que debe hacer un objeto para manejar elIDispatch cosas agrega mucha complejidad y ralentización a las llamadas de función.
  2. Cree una biblioteca C ++ estrechamente integrada y específica de Python utilizando modismos y estructuras de datos Pythonic: la idea de un Rowobjeto y el while cursor.Next():baile realmente extraño 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 a la función CPython (básicamente una __getitem__llamada, muy optimizada en las listas). Hacerlo row.getValue("column")en comparación es más pesado: hace una __getattr__búsqueda del método (en el que necesita crear un nuevo objeto de método enlazado), luego llama a ese método con los argumentos dados ( __call__). Cada parte de la arcpy.daimplementación está muy estrechamente integrada con la API de CPython con una gran cantidad de C ++ sintonizado a mano para que sea más rápido, utilizando estructuras de datos nativas de Python (y una integración complicada, también, para aún 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 más del doble de rápido que arcpy.daen la mayoría de las tareas. El uso de código Python arcpy.daes más rápido, pero aún no es más rápido que un lenguaje compilado de nivel inferior.

TL; DR : daes más rápido porque dase implementa en Arcobjects / C ++ / CPython directo, sin adulterar, que fue diseñado específicamente para generar un código Python rápido.

Jason Scheirer
fuente
4

Rendimiento relacionado

  • El cursor solo itera a través de la lista de campos establecida de forma predeterminada (no toda la base de datos)

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

  • Capacidad para usar tokens (por ejemplo, SHAPE @ LENGTH, SHAPE @ XY) para acceder a la geometría de la entidad
  • Capacidad para recorrer bases de datos (utilizando el método arcpy.da.Walk )
artwork21
fuente