#python #performance #function #optimization #cython
#python #Производительность #функция #оптимизация #cython
Вопрос:
Этот медленный код можно улучшить, изменив структуру, но иногда это сложно обойти. Я думаю, причина кроется в классах, хранящихся в массиве. Я слышал, что представления памяти используются для связывания массивов python и c, но я все еще новичок в этом (только некоторые знания python).
Есть ли способ эффективно выполнить следующее?
Пример класса:
cdef class ClassWithAdditionFunction:
cdef double value
def __init__(self, double value):
self.value = value
cpdef add_one(self):
self.value = 1
Медленная функция:
cdef unsigned long int i, ii
cdef unsigned long int loops = pow(10, 8)
cdef double value
addition_classes = np.array([None] * 10)
for i in range(len(addition_classes)):
addition_classes[i] = ClassWithAdditionFunction(value=0)
for i in range(loops/10):
for ii in range(10):
addition_classes[ii].add_one()
Большое вам спасибо за любые предложения!
Ответ №1:
Есть несколько небольших вещей, которые вы могли бы сделать, которые должны немного помочь. На самом деле строка кода, которую вы хотите ускорить, это addition_classes[ii].add_one()
. Если вы используете cython -a
, чтобы увидеть, что на самом деле происходит под капотом, вы увидите, что вы вызываете Pyx_GetItemInt , затем PyObject_GetAttr , затем PyObject_Call . Вы хотите структурировать свой код, чтобы избежать этих 3 вызовов.
Чтобы избежать вызова GetItem, вы захотите использовать либо интерфейс буфера numpy, либо представления памяти. Это сообщает cython структуру вашего массива и позволяет ему более эффективно извлекать элементы из массива. В приведенном ниже примере я использовал представление памяти. Если вы делаете что-то подобное, убедитесь, что массив на самом деле является массивом, полным экземпляров ClassWithAdditionFunction , иначе вы, скорее всего, получите ошибку segfault.
Чтобы избежать вызова GetAttr, объявите переменную типа ClassWithAdditionFunction и выполните вызовы метода для этой переменной, чтобы cython знал, что у переменной есть скомпилированная версия метода, которую он может использовать для более быстрых вызовов.
Наконец, вы уже определили add_one с помощью метода cpdef, но я бы предложил также добавить возвращаемый тип. Обычно мы могли бы просто поместить void , но поскольку это функция cpdef, а не функция cdef, вы можете вместо этого использовать int .
Если вы соберете все это вместе, это должно выглядеть примерно так:
import numpy as np
cimport cython
cdef class ClassWithAdditionFunction:
cdef double value
def __init__(self, double value):
self.value = value
cpdef int add_one(self):
self.value = 1
return 0
@cython.boundscheck(False)
@cython.wraparound(False)
def main():
cdef:
unsigned long int i, ii, loops = 10 ** 6
ClassWithAdditionFunction addInstance
double value, y
addition_classes = np.array([None] * 10)
cdef ClassWithAdditionFunction[:] arrayview = addition_classes
for i in range(len(addition_classes)):
addition_classes[i] = ClassWithAdditionFunction(value=0)
for i in range(loops/10):
for ii in range(10):
addInstance = arrayview[ii]
addInstance.add_one()
return None
Комментарии:
1. Это на удивление быстро! Большое вам спасибо!
2. @user3465201 вы можете принять ответ, нажав на стрелку слева…