我目前正在向cython迁移一组功能,这些功能目前已通过scipy.weave(现已弃用)在C ++中实现。
这些函数在输入和输出中都是二维列表的时间序列点上运行(例如[[17100,19.2],[17101、20.7],[17102、20.3]等)。一个示例函数是subtract
,它接受两个时间序列并计算一个新的时间表,以减去两个输入的日期。
必须保持结构和接口的兼容性,但是我的性能测试表明,Cython移植比原始scipy.weave实现的速度慢30%-40%。 我尝试了许多优化方法(对Numpy数组和memoryview的内部转换,C指针等),但是转换时间需要拉长整体执行时间。即使试图将输入和输出定义为C ++向量,利用Cython隐式转换似乎也无法保持scipy.weave速度。我还使用了关于boundscheck,环绕,除法...的各种提示。
最慢的情况似乎是在使用嵌套循环的函数上,我已经看到可以预定义列表大小(cdef list target = [[-1, float('nan')]]*size
)有所收获。
我知道Cython不能在Python结构(尤其是列表)上执行那么多的工作,但是还有其他可以提高速度的技巧或技术吗?
===编辑-添加代码示例=== 下面是函数类型学的一个很好的例子。 该函数获取日期/价格的2-D列表和日期/小数因子的2-D列表,并搜索两个列表之间的匹配日期,通过乘或除(即第三位)来计算相应价格/因子的输出输入参数)。
我表现最好的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);