我正在尝试学习cython,在那里我使用annotate=True
进行编译。
在基本手册中说:
如果一行是白色的,这意味着生成的代码不会与Python交互,因此运行速度将与普通C代码一样快。黄色越深,那一行中的Python交互就越多
然后,我在cython的基本手册说明中写下了以下代码(正如我所理解的(numpy:
+14: cdef entropy(counts):
15: '''
16: INPUT: pandas table with counts as obsN
17: OUTPUT: general entropy
18: '''
+19: cdef int l = counts.shape[0]
+20: cdef np.ndarray probs = np.zeros(l, dtype=np.float)
+21: cdef int totals = np.sum(counts)
+22: probs = counts/totals
+23: cdef np.ndarray plogp = np.zeros(l, dtype=np.float)
+24: plogp = ( probs.T * (np.log(probs)) ).T
+25: cdef float d = np.exp(-1 * np.sum(plogp))
+26: cdef float relative_d = d / probs.shape[0]
27:
+28: return {'d':d,
+29: 'relative_d':relative_d
30: }
其中所有的">+
"为黄色。
我做错了什么?如何使该函数的至少一部分以c速度运行?该函数返回一个python字典,因此我认为我不能返回任何c数据类型。我可能错了。
谢谢你的帮助!
首先,Cython不重写Numpy函数,它只是像CPython那样调用它们。这是例如np.zeros
、np.sum
或np.log
的情况。使用Cython,这样的呼叫不会更快。如果你想要一个更快的代码,你可以使用简单的循环来在你的代码中重新实现它们。然而,这可能不会更快:一方面,如果您使用小数组,并且每个函数都生成读取/写入速度通常很慢的巨大临时数组,Numpy调用会引入一定的开销(由于Cython、内部函数调用、包装器等仍然启用了类型检查AFAIK(;另一方面,一些Numpy函数使用高度优化的代码(如BLAS或低级SIMD内部函数(。此外,Python中的除法行为与C不同。这就是为什么Cython提供了可以设置为True
的标志cython.cdivision
(默认为False
(。如果使用Python分区,Cython会生成一个较慢的包装代码。最后,np.ndarray
是一个CPython类型,因此,您可以使用memoryviews来处理Numpy对象。
如果您想获得快速代码,您当然需要使用内存视图、循环和,避免创建临时数组以及使用多个线程。此外,在您的情况下,您可以使用np.empty
而不是np.zeros
。除此之外,Numpy转置不是很有效,并且Numpy不能解决这个问题。您可以实现平铺换位来加快它的速度,但这对于有效地实现它来说并不是微不足道的。这里有一个Numba实现,它当然可以很容易地转换为Cython代码。在PythonNumpy代码中放入一些cdef
通常不会使其更快。