Как производительность курсора доступа к данным настолько улучшена по сравнению с предыдущими версиями?

18

Модуль доступа к данным был представлен в ArcGIS версии 10.1. ESRI описывает модуль доступа к данным следующим образом ( источник ):

Модуль доступа к данным arcpy.da - это модуль Python для работы с данными. Он позволяет управлять сеансом редактирования, операцией редактирования, улучшенной поддержкой курсора (включая более высокую производительность), функциями для преобразования таблиц и классов пространственных объектов в массивы NumPy и из них, а также поддержкой рабочих процессов управления версиями, репликами, доменами и подтипами.

Однако очень мало информации о том, почему производительность курсоров так улучшена по сравнению с курсорами предыдущего поколения.

На прилагаемом рисунке показаны результаты теста производительности нового daметода UpdateCursor по сравнению со старым методом UpdateCursor. По сути, сценарий выполняет следующий рабочий процесс:

  1. Создать случайные точки (10, 100, 1000, 10000, 100000)
  2. Произвольная выборка из нормального распределения и добавление значения в новый столбец в таблице атрибутов случайных точек с помощью курсора
  3. Запустите 5 итераций каждого сценария случайных точек для нового и старого методов UpdateCursor и запишите среднее значение в списки
  4. График результатов

Что происходит за кулисами с daкурсором обновления, чтобы улучшить производительность курсора до степени, показанной на рисунке?


введите описание изображения здесь


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()
Аарон
источник

Ответы:

25

Один из разработчиков arcpy.daздесь. Мы получили производительность там, где она есть, потому что производительность была нашей главной заботой : главная проблема старых курсоров заключалась в том, что они были медленными, а не в том, что им не хватало какой-либо конкретной функциональности. В коде используются те же базовые объекты ArcObject, которые доступны в ArcGIS начиная с 8.x (например, реализация CPython для поискового курсора во многом похожа на примеры кода, подобные этой, за исключением того, что вы знаете, в C ++ вместо C #).

Вот две основные вещи, которые мы сделали для ускорения:

  1. Устраните уровни абстракции: первоначальная реализация курсора Python была основана на старом объекте GPDispatch на основе Dispatch / COM , который позволял использовать один и тот же API на любом языке, который мог использовать объекты COM Dispatch . Это означает, что у вас был API, который не был особенно хорошо оптимизирован для какой-либо отдельной среды, но это также означало, что было много уровней абстракции для объектов COM, например, для объявления и разрешения методов во время выполнения. Если вы помните, что до ArcGIS 9.3 можно было писать сценарии геообработки, используя тот же неуклюжий интерфейс для многих языков, даже для Perl и Ruby . Дополнительные документы, которые объект должен сделать, чтобы справиться сIDispatch вещи добавляют много сложности и замедления к вызовам функций.
  2. Сделайте тесно интегрированную, Python конкретные C ++ библиотеки с использованием вещих идиом и структур данных: идея с Rowобъектом и действительно странного while cursor.Next():танец была просто неэффективны в Python. Извлечение элемента из списка - это очень быстрая операция, которая упрощается до пары вызовов функций CPython (в основном, это __getitem__вызов, сильно оптимизированный для списков). Делать row.getValue("column")сравнения более тяжеловес: он делает , __getattr__чтобы принести метод (на котором необходимо создать новый связанный объект метод), а затем вызвать этот метод с заданными аргументами ( __call__). Каждая часть arcpy.daреализации очень тесно интегрирована с CPython API с большим количеством настраиваемых C ++, чтобы сделать его быстрым, с использованием собственных структур данных Python (и кроссовая интеграция, также для еще большей скорости и эффективности памяти).

Вы также заметите, что почти в любом тесте ( см., Например , эти слайды ) arcobjects в .Net и C ++ по-прежнему в два раза быстрее, чем arcpy.daв большинстве задач. Использование кода Python arcpy.daбыстрее, но все же не быстрее, чем скомпилированный язык более низкого уровня.

TL; DR : daбыстрее, потому что daреализован в прямом, неповрежденном Arcobjects / C ++ / CPython, который был специально разработан, чтобы привести к быстрому коду Python.

Джейсон Шайрер
источник
4

Производительность связана

  • Курсор перебирает только заданный список полей по умолчанию (не всю базу данных)

Другие, не связанные напрямую с производительностью, но приятные улучшения:

  • Возможность использовать токены (например, SHAPE @ LENGTH, SHAPE @ XY) для доступа к геометрии объекта
  • Возможность обхода баз данных (используя метод arcpy.da.Walk )
artwork21
источник