为什么 Cython 一直制作 python 个对象而不是 c?
Why does Cython keep making python objects instead of c?
我正在尝试学习 cython,我用 annotate=True
编译。
在The basic manual中说:
如果一行是白色的,这意味着生成的代码不与 Python 交互,因此 运行 与普通 C 代码一样快。黄色越深,该行中的 Python 交互越多
然后我按照(据我所知)numpy in cython basic manual 说明编写了这段代码:
+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: }
其中所有行首的"+
"在cython.debug.output.html中都是黄色文件。
我做错了什么?我怎样才能以 c 速度至少完成此功能的一部分 运行?
函数 returns 一个 python 字典,因此我认为我不能返回任何 c 数据类型。我这里可能是错的。
感谢您的帮助!
首先,Cython 不会重写 Numpy 函数,它只是像 CPython 那样调用它们。例如 np.zeros
、np.sum
或 np.log
就是这种情况。使用 Cython 时,此类调用不会更快。如果您想要更快的代码,您可以使用普通循环在您的代码中 重新实现 它们。但是,这可能不会更快:一方面,Numpy 调用会引入开销(由于类型检查 AFAIK 仍然启用了 Cython、内部函数调用、包装器等),如果您使用小数组并且每个函数生成巨大的临时数组,这肯定很重要往往 read/write 很慢;另一方面,一些 Numpy 函数使用 highly-optimized 代码(如 BLAS 或 low-level SIMD 内在函数)。此外,Python 中的除法与 C 的行为方式不同。这就是为什么 Cython 提供标志 cython.cdivision
可以设置为 True
(它是 False
默认)。如果使用 Python 除法,Cython 会生成较慢的包装代码。最后,np.ndarray
是 CPython 类型并且表现如此,您可以使用 memoryviews 所以不要处理 Numpy 对象。
如果你想获得快速的代码,你当然需要使用memoryviews、loops和avoid创建临时数组 以及使用 多线程。此外,您可以在您的情况下使用 np.empty
而不是 np.zeros
。除此之外,Numpy 转置效率不是很高,Numpy 没有解决这个问题。您可以实施 tiled-transposition 来加速它,但这对于有效实施它并非易事。 是一个 Numba 实现,当然可以轻松转换为 Cython 代码。将一些 cdef
放在 Python Numpy 代码上通常不会使其更快。
我正在尝试学习 cython,我用 annotate=True
编译。
在The basic manual中说:
如果一行是白色的,这意味着生成的代码不与 Python 交互,因此 运行 与普通 C 代码一样快。黄色越深,该行中的 Python 交互越多
然后我按照(据我所知)numpy in cython basic manual 说明编写了这段代码:
+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: }
其中所有行首的"+
"在cython.debug.output.html中都是黄色文件。
我做错了什么?我怎样才能以 c 速度至少完成此功能的一部分 运行? 函数 returns 一个 python 字典,因此我认为我不能返回任何 c 数据类型。我这里可能是错的。
感谢您的帮助!
首先,Cython 不会重写 Numpy 函数,它只是像 CPython 那样调用它们。例如 np.zeros
、np.sum
或 np.log
就是这种情况。使用 Cython 时,此类调用不会更快。如果您想要更快的代码,您可以使用普通循环在您的代码中 重新实现 它们。但是,这可能不会更快:一方面,Numpy 调用会引入开销(由于类型检查 AFAIK 仍然启用了 Cython、内部函数调用、包装器等),如果您使用小数组并且每个函数生成巨大的临时数组,这肯定很重要往往 read/write 很慢;另一方面,一些 Numpy 函数使用 highly-optimized 代码(如 BLAS 或 low-level SIMD 内在函数)。此外,Python 中的除法与 C 的行为方式不同。这就是为什么 Cython 提供标志 cython.cdivision
可以设置为 True
(它是 False
默认)。如果使用 Python 除法,Cython 会生成较慢的包装代码。最后,np.ndarray
是 CPython 类型并且表现如此,您可以使用 memoryviews 所以不要处理 Numpy 对象。
如果你想获得快速的代码,你当然需要使用memoryviews、loops和avoid创建临时数组 以及使用 多线程。此外,您可以在您的情况下使用 np.empty
而不是 np.zeros
。除此之外,Numpy 转置效率不是很高,Numpy 没有解决这个问题。您可以实施 tiled-transposition 来加速它,但这对于有效实施它并非易事。 cdef
放在 Python Numpy 代码上通常不会使其更快。