Cythonizing конкретных методов класса, написанного на Python

У меня есть алгоритм планирования *, написанный исключительно на 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 ++, чтобы знать, что делать.

1

Решение

Задача ещё не решена.

Другие решения

Других решений пока нет …