2014-01-07 42 views
8

考慮這個啞用Cython代碼:用Cython Memoryview作爲返回值

#!python 
#cython: boundscheck=False 
#cython: wraparound=False 
#cython: initializedcheck=False 
#cython: cdivision=True 
#cython: nonecheck=False 

import numpy as np 

# iterator function 
cdef double[:] f(double[:] data): 
    data[0] *= 1.01 
    data[1] *= 1.02 
    return data 

# looping function 
cdef double[:] _call_me(int bignumber, double[:] data): 
    cdef int ii 
    for ii in range(bignumber): 
     data = f(data) 
    return data 

# helper function to allow calls from Python 
def call_me(bignumber): 
    cdef double[:] data = np.ones(2) 
    return _call_me(bignumber, data) 

現在,如果我做了用Cython -a這一點,它用黃色顯示return語句。我在一個非常關鍵的程序中做了類似的事情,根據分析,這實際上減慢了我的代碼速度。那麼,爲什麼cython需要這些返回語句的python?附加說明文件給出了一個暗示:

PyErr_SetString(PyExc_TypeError,"Memoryview return value is not initialized"); 

令人驚訝的是,谷歌搜索用Cython「未初始化Memoryview返回值」給出結果爲零。

+0

Cython版本0.19.2 – HenriV

+0

在你真實的代碼中,你需要返回memoryview還是可以像這樣修改它?做這些改變使我有40倍的加速。我不確定是否有辦法切換檢查... – jorgeca

+0

真正的代碼迭代求解常微分方程,所以是的,我確實需要返回它。 – HenriV

回答

3

緩慢的部分不是你想象的那樣。緩慢的部分是(主要...)

data = f(data) 

不是f(data)data =

此分配struct,其被定義爲這樣

typedef struct { 
    struct __pyx_memoryview_obj *memview; 
    char *data; 
    Py_ssize_t shape[8]; 
    Py_ssize_t strides[8]; 
    Py_ssize_t suboffsets[8]; 
} __Pyx_memviewslice; 

和提到的分配確實

__pyx_t_3 = __pyx_f_3cyt_f(__pyx_v_data); 

其中__pyx_t_3是該類型的。如果這樣做是在一個循環中完成的,那麼複製結構所花費的時間要遠遠大於函數的平凡體。我已經在純C中完成了一個時間,它給出了類似的數字。

編輯注:該分配實際上主要是一個問題,因爲它也導致代結構和其他副本的不被優化掉了。)

然而,整個事情似乎傻了。複製結構的唯一原因是如果事情發生了變化,但沒有任何結果。記憶指向同一地點,同一地點的數據點和形狀,步幅和偏移量相同。

我看到避免struct副本的唯一方法是不改變它所引用的任何東西(也就是總是返回memoryview)。這隻有在無論如何返回毫無意義的情況下才可能,就像這裏。或者你可以在C上進行攻擊,我想,就像我一樣。如果你打破了某件事,別哭。


另外請注意,你可以讓你的函數nogil,所以它不能有任何與聯想起了Python。


編輯

C'S優化編譯器被扔我稍微偏離。基本上,我刪除了一些分配,並刪除了其他東西的負載。基本上慢的路徑是這樣的:

#include<stdio.h> 


struct __pyx_memoryview_obj; 


typedef struct { 
    struct __pyx_memoryview_obj *memview; 
    char *data; 
    ssize_t shape[8]; 
    ssize_t strides[8]; 
    ssize_t suboffsets[8]; 
} __Pyx_memviewslice; 


static __Pyx_memviewslice __pyx_f_3cyt_f(__Pyx_memviewslice __pyx_v_data) { 
    __Pyx_memviewslice __pyx_r = { 0, 0, { 0 }, { 0 }, { 0 } }; 
    __pyx_r = __pyx_v_data; 
    return __pyx_r; 
} 

main() { 
    int i; 
    __Pyx_memviewslice __pyx_v_data = {0, 0, { 0 }, { 0 }, { 0 }}; 

    for (i=0; i<10000000; i++) { 
     __pyx_v_data = __pyx_f_3cyt_f(__pyx_v_data); 
    } 
} 

(編譯沒有優化)。我不是C程序員,所以我非常抱歉,如果我所做的一切都與我複製計算機生成代碼的事實不直接相關。

我知道這不是幫助,但我盡力了,好嗎?

+2

+1表示這比我想象的更復雜,並且對於nogil提示。 – HenriV