我可以进一步优化这个 Cython 代码片段(包含 NumPy 和一个字符串)吗?
Can I further optimize this Cython code snippet (contains NumPy and a string)?
我是 Cython 的初学者,但我一直在学习一些入门教程以尝试优化一些 Python 代码。我已经为所有变量定义了类型,但 cython --annotate
显示的 Python 交互比预期的多得多。这是输出,对于那些无法查看图像的人,还有代码:
+148: cdef int[:] get_hp_lengths(str seq):
+149: hp_lens_buf = np.zeros(len(seq), dtype=np.intc)
+150: cdef int[:] hp_lens = hp_lens_buf
151: cdef Py_ssize_t start, stop
152:
+153: for start in range(len(seq)):
+154: for stop in range(start+1, len(seq)):
+155: if seq[stop] != seq[start]:
+156: hp_lens[start] = stop - start
+157: break
+158: if len(seq):
+159: hp_lens[-1] += 1
+160: return hp_lens
是不是因为我用的是numpy
,应该已经够快了吧?看起来 str
也引起了 Python 交互。有没有更好的数据类型可以使用,因为我先验地不知道 seq
的长度,但它只包含 'ACGT'
?基本上,我想验证我是否遵循了最佳实践,没有比这更快的方法来实现了。
大多数 python 交互是由于 len
是一个 python 函数并且您多次调用它。但是,它只需要计算一次。您可以通过 boundscheck
编译器指令禁用对 hp_lens[start]
的索引检查来进一步减少 python 交互:
from cython cimport boundscheck
@boundscheck(False)
cdef int[:] get_hp_lengths(str seq):
cdef int seq_len = len(seq)
hp_lens_buf = np.zeros(seq_len, dtype=np.intc)
cdef int[:] hp_lens = hp_lens_buf
cdef Py_ssize_t start, stop
for start in range(seq_len):
for stop in range(start+1, seq_len):
if seq[stop] != seq[start]:
hp_lens[start] = stop - start
break
if seq_len > 0:
hp_lens[-1] += 1
return hp_lens
这可能是您可以用代码做的最好的事情了:
- 将 python 字符串替换为
c_string
- 用连续的缓冲区替换内存缓冲区
- ??
- 盈利!
这是编辑后的代码
cimport cython
@cython.boundscheck(False)
cdef int[::1] get_hp_lengths(const unsigned char[::1] seq):
cdef int seq_len = seq.shape[0]
cdef int[::1] hp_lens = np.zeros(seq_len, dtype= np.intc)
cdef size_t start, stop
for start in range(seq_len):
for stop in range(start+1, seq_len):
if seq[stop] != seq[start]:
hp_lens[start] = stop - start
break
if seq_len > 0:
hp_lens[seq_len] += 1
return hp_lens
产生以下内容html
看起来不错!
我是 Cython 的初学者,但我一直在学习一些入门教程以尝试优化一些 Python 代码。我已经为所有变量定义了类型,但 cython --annotate
显示的 Python 交互比预期的多得多。这是输出,对于那些无法查看图像的人,还有代码:
+148: cdef int[:] get_hp_lengths(str seq):
+149: hp_lens_buf = np.zeros(len(seq), dtype=np.intc)
+150: cdef int[:] hp_lens = hp_lens_buf
151: cdef Py_ssize_t start, stop
152:
+153: for start in range(len(seq)):
+154: for stop in range(start+1, len(seq)):
+155: if seq[stop] != seq[start]:
+156: hp_lens[start] = stop - start
+157: break
+158: if len(seq):
+159: hp_lens[-1] += 1
+160: return hp_lens
是不是因为我用的是numpy
,应该已经够快了吧?看起来 str
也引起了 Python 交互。有没有更好的数据类型可以使用,因为我先验地不知道 seq
的长度,但它只包含 'ACGT'
?基本上,我想验证我是否遵循了最佳实践,没有比这更快的方法来实现了。
大多数 python 交互是由于 len
是一个 python 函数并且您多次调用它。但是,它只需要计算一次。您可以通过 boundscheck
编译器指令禁用对 hp_lens[start]
的索引检查来进一步减少 python 交互:
from cython cimport boundscheck
@boundscheck(False)
cdef int[:] get_hp_lengths(str seq):
cdef int seq_len = len(seq)
hp_lens_buf = np.zeros(seq_len, dtype=np.intc)
cdef int[:] hp_lens = hp_lens_buf
cdef Py_ssize_t start, stop
for start in range(seq_len):
for stop in range(start+1, seq_len):
if seq[stop] != seq[start]:
hp_lens[start] = stop - start
break
if seq_len > 0:
hp_lens[-1] += 1
return hp_lens
这可能是您可以用代码做的最好的事情了:
- 将 python 字符串替换为
c_string
- 用连续的缓冲区替换内存缓冲区
- ??
- 盈利!
这是编辑后的代码
cimport cython
@cython.boundscheck(False)
cdef int[::1] get_hp_lengths(const unsigned char[::1] seq):
cdef int seq_len = seq.shape[0]
cdef int[::1] hp_lens = np.zeros(seq_len, dtype= np.intc)
cdef size_t start, stop
for start in range(seq_len):
for stop in range(start+1, seq_len):
if seq[stop] != seq[start]:
hp_lens[start] = stop - start
break
if seq_len > 0:
hp_lens[seq_len] += 1
return hp_lens
产生以下内容html
看起来不错!