我通过添加一些类型并编译它来转换为cython python函数。 我在python和cython函数的结果之间得到了很小的数值差异。 经过一些工作后,我发现差异来自于使用unsigned int而不是int来访问numpy数组。
我使用unsigned int索引来加速访问:http://docs.cython.org/src/userguide/numpy_tutorial.html#tuning-indexing-further
无论如何,我认为使用无符号的整数是无害的。请参阅此代码:
cpdef function(np.ndarray[np.float32_t, ndim=2] response, max_loc):
cdef unsigned int x, y
x, y = int(max_loc[0]), int(max_loc[1])
x2, y2 = int(max_loc[0]), int(max_loc[1])
print response[y,x], type(response[y,x]), response.dtype
print response[y2,x2], type(response[y2,x2]), response.dtype
print 2*(response[y,x] - min(response[y,x-1], response[y,x+1]))
print 2*(response[y2,x2] - min(response[y2,x2-1], response[y2,x2+1]))
打印:
0.959878861904 <type 'float'> float32
0.959879 <type 'numpy.float32'> float32
1.04306024313
1.04306030273
为什么会这样?!!!这是一个错误吗?
好的,这里要求的是一个SSCCE,它与我在原始函数中使用的类型和值相同
cpdef function():
cdef unsigned int x, y
max_loc2 = np.asarray([ 15., 25.], dtype=float)
cdef np.ndarray[np.float32_t, ndim=2] response2 = np.zeros((49,49), dtype=np.float32)
x, y = int(max_loc2[0]), int(max_loc2[1])
x2, y2 = int(max_loc2[0]), int(max_loc2[1])
response2[y,x] = 0.959878861904
response2[y,x-1] = 0.438348740339
response2[y,x+1] = 0.753262758255
print response2[y,x], type(response2[y,x]), response2.dtype
print response2[y2,x2], type(response2[y2,x2]), response2.dtype
print 2*(response2[y,x] - min(response2[y,x-1], response2[y,x+1]))
print 2*(response2[y2,x2] - min(response2[y2,x2-1], response2[y2,x2+1]))
打印
0.959878861904 <type 'float'> float32
0.959879 <type 'numpy.float32'> float32
1.04306024313
1.04306030273
我使用python 2.7.3 cython 0.18和msvc9 express
答案 0 :(得分:7)
我修改了问题中的示例,以便更简单地读取模块生成的C源代码。我只对查看创建Python float
对象的逻辑感兴趣,而不是从np.float32
数组中获取response
个对象。
我使用pyximport
编译扩展模块。它将生成的C文件保存在~/.pyxbld
的子目录中(在Windows上可能为%userprofile%\.pyxbld
)。
import numpy as np
import pyximport
pyximport.install(setup_args={'include_dirs': [np.get_include()]})
open('_tmp.pyx', 'w').write('''
cimport numpy as np
cpdef function(np.ndarray[np.float32_t, ndim=2] response, max_loc):
cdef unsigned int p_one, q_one
p_one = int(max_loc[0])
q_one = int(max_loc[1])
p_two = int(max_loc[0])
q_two = int(max_loc[1])
r_one = response[q_one, p_one]
r_two = response[q_two, p_two]
''')
import _tmp
assert(hasattr(_tmp, 'function'))
这里是为感兴趣的部分生成的C代码(稍微重新格式化以使其更易于阅读)。事实证明,当您使用C unsigned int
索引变量时,生成的代码直接从数组缓冲区中获取数据并调用PyFloat_FromDouble
,这会将其强制转换为double
。另一方面,当您使用Python int
索引变量时,它采用通用方法。它形成一个元组并调用PyObject_GetItem
。这种方式允许ndarray
正确兑现np.float32
dtype。
#define __Pyx_BufPtrStrided2d(type, buf, i0, s0, i1, s1) \
(type)((char*)buf + i0 * s0 + i1 * s1)
/* "_tmp.pyx":9
* p_two = int(max_loc[0])
* q_two = int(max_loc[1])
* r_one = response[q_one, p_one] # <<<<<<<<<<<<<<
* r_two = response[q_two, p_two]
*/
__pyx_t_3 = __pyx_v_q_one;
__pyx_t_4 = __pyx_v_p_one;
__pyx_t_5 = -1;
if (unlikely(__pyx_t_3 >= (size_t)__pyx_bshape_0_response))
__pyx_t_5 = 0;
if (unlikely(__pyx_t_4 >= (size_t)__pyx_bshape_1_response))
__pyx_t_5 = 1;
if (unlikely(__pyx_t_5 != -1)) {
__Pyx_RaiseBufferIndexError(__pyx_t_5);
{
__pyx_filename = __pyx_f[0];
__pyx_lineno = 9;
__pyx_clineno = __LINE__;
goto __pyx_L1_error;
}
}
__pyx_t_1 = PyFloat_FromDouble((
*__Pyx_BufPtrStrided2d(
__pyx_t_5numpy_float32_t *,
__pyx_bstruct_response.buf,
__pyx_t_3, __pyx_bstride_0_response,
__pyx_t_4, __pyx_bstride_1_response)));
if (unlikely(!__pyx_t_1)) {
__pyx_filename = __pyx_f[0];
__pyx_lineno = 9;
__pyx_clineno = __LINE__;
goto __pyx_L1_error;
}
__Pyx_GOTREF(__pyx_t_1);
__pyx_v_r_one = __pyx_t_1;
__pyx_t_1 = 0;
/* "_tmp.pyx":10
* q_two = int(max_loc[1])
* r_one = response[q_one, p_one]
* r_two = response[q_two, p_two] # <<<<<<<<<<<<<<
*/
__pyx_t_1 = PyTuple_New(2);
if (unlikely(!__pyx_t_1)) {
__pyx_filename = __pyx_f[0];
__pyx_lineno = 10;
__pyx_clineno = __LINE__;
goto __pyx_L1_error;
}
__Pyx_GOTREF(((PyObject *)__pyx_t_1));
__Pyx_INCREF(__pyx_v_q_two);
PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_v_q_two);
__Pyx_GIVEREF(__pyx_v_q_two);
__Pyx_INCREF(__pyx_v_p_two);
PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_v_p_two);
__Pyx_GIVEREF(__pyx_v_p_two);
__pyx_t_2 = PyObject_GetItem(
((PyObject *)__pyx_v_response),
((PyObject *)__pyx_t_1));
if (!__pyx_t_2) {
__pyx_filename = __pyx_f[0];
__pyx_lineno = 10;
__pyx_clineno = __LINE__;
goto __pyx_L1_error;
}
__Pyx_GOTREF(__pyx_t_2);
__Pyx_DECREF(((PyObject *)__pyx_t_1));
__pyx_t_1 = 0;
__pyx_v_r_two = __pyx_t_2;
__pyx_t_2 = 0;
答案 1 :(得分:2)
在我的机器上玩这个,我看不出有什么区别。我正在使用带有cython magic的ipython笔记本:
In [1]:
%load_ext cythonmagic
In [12]:
%%cython
import numpy as np
cimport numpy as np
cpdef function(np.ndarray[np.float32_t, ndim=2] response, max_loc):
cdef unsigned int x, y
x, y = int(max_loc[0]), int(max_loc[1])
x2, y2 = int(max_loc[0]), int(max_loc[1])
#return 2*(response[y,x] - min(response[y,x-1], response[y,x+1])), 2*(response[y2,x2] - min(response[y2,x2-1], response[y2,x2+1]))
print response[y,x], type(response[y,x]), response.dtype
print response[y2,x2], type(response[y2,x2]), response.dtype
print 2*(response[y,x] - min(response[y,x-1], response[y,x+1]))
print 2*(response[y2,x2] - min(response[y2,x2-1], response[y2,x2+1]))
In [13]:
a = np.random.normal(size=(10,10)).astype(np.float32)
m = [3,2]
function(a,m)
0.586090564728 <type 'float'> float32
0.586091 <type 'numpy.float32'> float32
4.39655685425
4.39655685425
第一对结果,区别仅在于print语句的输出精度。您使用的是什么版本的Cython?索引极不可能影响答案,因为它只是访问numpy数组的数据属性存储的固定长度的内存。