У меня есть алгоритм планирования *, написанный исключительно на Python (используя этот превосходный ресурс на пути планирования). Класс сетки со связанными методами для движения и затрат определяется следующим образом:
class SquareGrid:
def __init__(self, width, height):
self.width = width
self.height = height
self.walls = []
self.weights = []
def in_bounds(self, id):
(x, y) = id
return 0 <= x < self.width and 0 <= y < self.height
def passable(self, id):
return id not in self.walls
def neighbors(self, id):
(x, y) = id
results = [(x + 1, y), (x + 1, y - 1), (x, y - 1), (x - 1, y - 1), (x - 1, y), (x - 1, y + 1), (x, y + 1), (x + 1, y + 1)]
if (x + y) % 2 == 0: results.reverse() # aesthetics
results = [r for r in results if self.in_bounds(r)]
results = [r for r in results if self.passable(r)] # this is where things slow down
return results
def cost(self, from_node, to_node):
return (to_node[0] - from_node[0])**2 + (to_node[1] - from_node[1])**2
Теперь я бы хотел немного ускорить выполнение, используя все преимущества статического компилятора Cython. Одним из вариантов было бы переписать все это с помощью статической типизации в Cython. Я профилировал чистый код Python, используя cProfiler, чтобы увидеть узкое место, и, что неудивительно, около 70% общего времени выполнения ушло на neighbors
метод (который вычисляет действительные соседние узлы вокруг текущего узла). Более конкретно, строка понимания списка в neighbors
звонки passable
более 33 000 раз для данного примера игрушек. passable
проверяет, помечен ли узел, указанный в его аргументе, как «препятствие», или нет, просматривая SquareGrid
Знание препятствий (SquareGrid.walls
, список кортежей местоположения) и возвращает логическое значение соответственно. Мне казалось, что, просто оптимизировав этот конкретный метод, я получу значительный выигрыш в скорости. Итак, я приступил к переписыванию passable
в Cython.
Я полный нуб на Cython и C / C ++ в целом, поэтому я был бы признателен, если бы была указана какая-либо ошибка в понимании того, как эта штука на самом деле работает. Я создал файл Pyrex passable_cy.pyx
с надеждой, что его скомпилируют с использованием Cython / GCC ++, а затем связывают с SquareGrid
объекта в основном скрипте будет достаточно. Вот как я определил passable_cy.pyx
:
cpdef bint passable(object self, tuple id):
return id not in self.walls
Сопровождающий setup.py
файл:
from distutils.core import setup
from Cython.Build import cythonize
setup(name="cynthonized_passable", ext_modules=cythonize(['passable_cy.pyx']),)
И вот как я связал новый синтезированный метод с SquareGrid
в основном скрипте A * python:
g = SquareGrid(100, 100) # create grid with dimensions 100x100
g.passable = types.MethodType(passable_cy.passable, g)
Все правильно скомпилировано и все выполнено без проблем. Там не было никакого улучшения скорости вообще, что я вроде ожидал (казалось слишком простым). Как мне продолжить отсюда? Является ли этот метод привязки лучшим способом? Я уверен, что больше можно сделать в passable_cy.pyx
, но я слишком незнаком с C / C ++, чтобы знать, что делать.
Задача ещё не решена.
Других решений пока нет …