如何减少Cython编译代码';s与Python的交互
我试图通过使用Cython编译为C来减少一段计算量特别大的Python代码。令人惊讶的是,我几乎没有成功地加快速度。对于原始Python模块,代码运行时间约为45秒,对于Cython编译模块,代码运行时间约为45秒 使用如何减少Cython编译代码';s与Python的交互,python,cython,Python,Cython,我试图通过使用Cython编译为C来减少一段计算量特别大的Python代码。令人惊讶的是,我几乎没有成功地加快速度。对于原始Python模块,代码运行时间约为45秒,对于Cython编译模块,代码运行时间约为45秒 使用annotate=True编译时,我留下了大量黄色(ish)行,这表明仍然存在大量Python交互 对此,我关闭了boundscheck(False)和cdivision(True)。这没有效果。下面的代码片段是此模块的摘录,它坚持与Python交互。为什么会这样?这里没有任何东
annotate=True
编译时,我留下了大量黄色(ish)行,这表明仍然存在大量Python交互
对此,我关闭了boundscheck(False)
和cdivision(True)
。这没有效果。下面的代码片段是此模块的摘录,它坚持与Python交互。为什么会这样?这里没有任何东西需要与任何预先存在的Python模块交互,它都是非常简单的算术模块
cpdef float __distance_between(point1, point2) except? -2:
return ((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2) ** (1/2)
point1
和point2
是Python列表,每个列表都包含2个double
s,例如:[611811.997,-871083.372]
Cython为代码段中的返回
行生成的C
-代码如下:
+05: cpdef float __distance_between(point1, point2) except? -2:
+06: return ((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2)**(1/2)
__pyx_t_1 = __Pyx_GetItemInt(__pyx_v_point1, 0, long, 1, __Pyx_PyInt_From_long, 0, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 6, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_1);
__pyx_t_2 = __Pyx_GetItemInt(__pyx_v_point2, 0, long, 1, __Pyx_PyInt_From_long, 0, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 6, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_2);
__pyx_t_3 = PyNumber_Subtract(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 6, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_3);
__Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
__Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
__pyx_t_2 = PyNumber_Power(__pyx_t_3, __pyx_int_2, Py_None); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 6, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_2);
__Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
__pyx_t_3 = __Pyx_GetItemInt(__pyx_v_point1, 1, long, 1, __Pyx_PyInt_From_long, 0, 0, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 6, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_3);
__pyx_t_1 = __Pyx_GetItemInt(__pyx_v_point2, 1, long, 1, __Pyx_PyInt_From_long, 0, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 6, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_1);
__pyx_t_4 = PyNumber_Subtract(__pyx_t_3, __pyx_t_1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 6, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_4);
__Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
__Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
__pyx_t_1 = PyNumber_Power(__pyx_t_4, __pyx_int_2, Py_None); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 6, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_1);
__Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
__pyx_t_4 = PyNumber_Add(__pyx_t_2, __pyx_t_1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 6, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_4);
__Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
__Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
__pyx_t_1 = __Pyx_PyInt_From_long((1 / 2)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 6, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_1);
__pyx_t_2 = PyNumber_Power(__pyx_t_4, __pyx_t_1, Py_None); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 6, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_2);
__Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
__Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
__pyx_t_5 = __pyx_PyFloat_AsFloat(__pyx_t_2); if (unlikely((__pyx_t_5 == (float)-1) && PyErr_Occurred())) __PYX_ERR(0, 6, __pyx_L1_error)
__Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
__pyx_r = __pyx_t_5;
goto __pyx_L0;
如何更好地优化代码或编译,使此代码段独立于Python
本模块中有许多其他行都呈现为黄色,但从这个问题开始,可能会让我更好地理解如何处理其余部分。您没有
cdef
编辑任何变量;你知道它们是浮动的,Cython不知道。说出来
cpdef float __distance_between((double, double)point1, (double, double)point2) except? -2:
return ((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2) ** (1/2)
旁注:确保编译时使用了
-3
以选择Python 3语法,否则1/2
将被解释为0
;或者,只需将其更改为literal0.5
(或1./2.
或诸如此类),无论您使用哪种语法(Py2与Py3),它都会工作。是的,这就是问题所在。感谢您在1/2
上的指针。但是,如果这些点是真正的python浮点数列表,那么没有一个Cython数据类型可以有效地表达这一点。您刚刚将工作转移到函数调用中的一些转换中。@DavidW:这仍然会有意义地提高性能。当然,您需要提前进行转换,但它会将一堆PyNumber
通用API调用转换为原始C操作。四个解包和一个重新打包并不好,但在这两者之间都是原始的Cdouble
操作,比六个通用的PyNumber
操作要好得多(每个操作都必须解包两个操作数,执行数学运算,并将结果重新打包到Pythonfloat
).@ShadowRanger是的,公平点-我认为你是对的,它会有所帮助,我认为这是在不知道OP如何使用函数的情况下进行优化所能做到的。