优化在python列表上运行的cython函数

时间:2018-12-02 10:28:05

标签: python scipy cython

我目前正在向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);

0 个答案:

没有答案