Три виновника из вашего профилирования выглядят как Node's __init__
(что здесь неизбежно) и __get__
и __set__
для свойства prev_xor_next
. На мой взгляд, вам не нужно свойство prev_xor_next
(или если вы это делаете, оно должно быть доступно только для чтения), поскольку оно делает доступным внутреннее содержимое Cython в Python.
Независимо от того, удаляется ли свойствоили нет, вы здесь работаете в Cython, поэтому вы можете писать напрямую в базовый атрибут C _prev_xor_next
. Возможно, вам придется установить cdef Node cur_last
в начале append
(и, возможно, в других функциях), чтобы Cython знал тип cur_last
- я думаю, что он сможет его решить, но если вы получите AttributeErrors
во время выполнения, это то, что вам нужно сделать.
Это изменение дает мне увеличение скорости на 30% (т.е. оно все еще медленнее, чем обычный список, но это заметное улучшение).
Я опишу более радикальные изменения, которые я, возможно, следовало бы предложить в вашем первом вопросе об этой проблеме. Это действительно расплывчатая схема, поэтому не было предпринято никаких усилий, чтобы заставить его работать ...
Node
полностью соответствует вашему XORList
классу: его не следует использоватьот Python и время жизни всех Nodes
в XORList
напрямую связано со списком. Поэтому они должны быть уничтожены при уничтожении принадлежащего им XORList
(или, если список сокращается и т. Д.), И поэтому нет необходимости в подсчете ссылок. Следовательно, Node
должен быть структурой C, а не объектом Python:
cdef struct Node:
uintptr_t prev_xor_next
PyObject* val
# with associated constructor- and destructor-like functions:
cdef Node* make_node(object val, uintptr_t prev_xor_next):
cdef Node* n = <Node*>malloc(sizeof(Node))
n.val = <PyObject*>val
Py_XINCREF(n.val)
n.prev_xor_next = prev_xor_next
return n
cdef void destroy_node(Node* n):
Py_XDECREF(n.val)
free(n)
XORList
нуждается в функции __dealloc__
, которая перебирает список, вызывая destroy_node
для каждогоNode
(в любом случае в вашей версии ему также требуется функция __dealloc__
!)
CurrentNode
должен оставаться классом Cython, поскольку это ваш интерфейс "итератора". Очевидно, он больше не может наследовать от Node
. Я бы изменил его на:
cdef class XORListIterator:
cdef Node* current_node
cdef XORList our_list
смысл атрибута our_list
состоит в том, чтобы гарантировать, что XORList
сохраняется как минимум столько же, сколько CurrentNode
- если вы в конечном итоге ситератор для XORList
, который больше не существует, и атрибут current_node
будет недействительным. current_node
не принадлежит XORListIterator
, поэтому нет необходимости в деструкторе.
Я думаю, опасность этой схемы заключается в том, что если какие-либо изменения в XORList
не будутt полностью лишить законной силы любые существующие XORListIterators
до точки, где вы получаете сбой. Я подозреваю, что это также будет проблемой с вашей текущей версией.
Я подозреваю, что встроенный list
все равно останется конкурентоспособным, так как это хорошо написанная, эффективная структура. Помните, что list.append
обычно является простым Py_INCREF
, со случайным перераспределением и копированием массива. Ваша всегда включает создание нового объекта Python (Node
), а также некоторый связанный подсчет ссылок.
Моя альтернативная схема позволяет избежать большого количества подсчетов ссылок (как с точки зрения вычислительного времени, так и «вам нужноподумайте об этом "время", так что я ожидаю, что это будет гораздо ближе. Это сохраняет недостаток небольшого выделения памяти для каждого append
, что неизбежно для структуры связанного списка.
Добавление : для комментария о "удобствекласс Cython ". На мой взгляд, два преимущества использования класса Cython перед структурой:
- Вы получаете что-то довольно близкое к структуре, но вам не нужно беспокоиться о C-указателях, и подсчет ссылок взятзабота о. Совершенно очевидно, что для этой проблемы вы делаете странные вещи с указателями и вынуждены обрабатывать подсчет ссылок явно, поэтому я не думаю, что это относится к вам.
- Вы можете использовать это из Python - вы нене ограничивается только Cython. В этом случае я думаю, что это полностью детали реализации
XORList
, которые не должны быть представлены пользователям Python.
Поэтому я думаю, что основные причины использовать классы Cython специально don 't применимо к вашей проблеме. (Конечно, к большому количеству кода эти преимущества применимы!)
Вероятно, также стоит добавить, что построение классов Cython, вероятно, является одной из их более медленных функций - для поддержки возможного наследования процесс построения является довольно "косвенным". Вам удалось создать бенчмарк, который оказывается практически полностью построенным - я думаю, это слегка искаженный бенчмарк, и реальный случай может быть не таким уж плохим.