В настоящее время я перевожу на Cython набор функций, которые в настоящее время реализованы в C ++ через scipy.weave (в настоящее время не рекомендуется).Эти функции работают с точками временных рядов, которые являются 2D-списками (например, [[17100, 19.2], [17101, 20.7], [17102, 20.3], ...]) как на входе, так и на выходе.Примером функции является subtract
, которая принимает две временные серии и рассчитывает новую временную серию как вычитание двух входных данных, идущих от даты к дате.
Структура и интерфейсы должны поддерживаться для ретро-совместимости, но мое профилированиеИспытания показывают, что перенос Cython на 30-40% медленнее, чем в оригинальной реализации scipy.weave.Я перепробовал много способов оптимизации (внутренние преобразования в массивы Numpy и представления памяти, указатели C и т. Д.), Но необходимое время преобразования увеличивает общее время выполнения.Даже пытаясь определить вход и выход как векторы C ++, использование неявных преобразований Cython не кажется эффективным для поддержания скорости scipy.weave.Я также использовал различные подсказки для Boundscheck, Wraparound, Division, ...
Наибольшие замедления, кажется, для функций, которые используют вложенные циклы, и я видел, что небольшое усиление может предопределятьразмер списка (cdef list target = [[-1, float('nan')]]*size
).
Я знаю, что Cython не может так много выполнять на структурах Python, особенно на списках, но есть ли другие приемы или приемы, с помощью которых можно получить ускорение?
=== РЕДАКТИРОВАТЬ - ДОБАВИТЬ ПРИМЕР КОДА === Хорошим примером типологии функций является следующее.Функция берет двумерный список дат / цен и двумерный список дат / десятичных коэффициентов и ищет совпадающие даты между двумя списками, вычисляя выходную информацию по соответствующей цене / коэффициенту путем умножения или деления (это третьвходной параметр).
Мой самый эффективный код Cython:
@cython.cdivision(True)
@cython.boundscheck(False)
@cython.wraparound(False)
cpdef apply_conversion(list original_timeserie, list factor_timeserie, int divide_or_multiply=False):
cdef:
Py_ssize_t i, j = 0, size = len(original_timeserie), size2 = len(factor_timeserie)
long original_date, factor_date
double original_price, factor_price, conv_price
list result = []
for i in range(size):
original_date = original_timeserie[i][0]
for j in range(j, size2):
factor_date = factor_timeserie[j][0]
if original_date == factor_date:
original_price = original_timeserie[i][1]
factor_price = factor_timeserie[j][1]
if divide_or_multiply:
if factor_price != 0:
conv_price = original_price / factor_price
else:
conv_price = float('inf')
else:
conv_price = original_price * factor_price
result.append([original_date, conv_price])
break
return result
Оригинальная функция scipy:
int len = original_timeserie.length();
int len2 = factor_timeserie.length();
PyObject* py_serieconv = PyList_New(len);
PyObject* original_item = NULL;
PyObject* factor_item = NULL;
PyObject* date = NULL;
PyObject* value = NULL;
long original_date = 0;
long factor_date = 0;
double original_price = 0;
double factor_price = 0;
int j = 0;
for(int i=0;i<len;i++) {
original_item = PyList_GetItem(original_timeserie, i);
date = PyList_GetItem(original_item, 0);
original_date = PyInt_AsLong(date);
original_price = PyFloat_AsDouble( PyList_GetItem(original_item, 1) );
factor_item = NULL;
for(;j<len2;) {
factor_item = PyList_GetItem(factor_timeserie, j++);
factor_date = PyInt_AsLong(PyList_GetItem(factor_item, 0));
if (factor_date == original_date) {
factor_price = PyFloat_AsDouble(PyList_GetItem(factor_item, 1));
value = PyFloat_FromDouble(original_price * (divide_or_multiply==0 ? factor_price : 1/factor_price));
PyObject* py_new_item = PyList_New(2);
Py_XINCREF(date);
PyList_SetItem(py_new_item, 0, date);
PyList_SetItem(py_new_item, 1, value);
PyList_SetItem(py_serieconv, i, py_new_item);
break;
}
}
}
return_val = py_serieconv;
Py_XDECREF(py_serieconv);