【问题标题】:Cythonize list of all splits of a stringCythonize 字符串所有拆分的列表
【发布时间】:2017-04-22 00:08:45
【问题描述】:

我正在尝试加快一段代码,该代码会生成所有可能的字符串拆分。

splits('foo') -> [('f', 'oo'), ('fo', 'o'), ('foo', '')]

python中的代码很简单:

def splits(text):
    return [(text[:i + 1], text[i + 1:])
            for i in range(len(text))]

有没有办法通过 cython 或其他方式加快速度?就上下文而言,此代码的更大目的是找到具有最高概率的字符串拆分。

【问题讨论】:

  • 你以前用 Cython 做过什么吗?在 Cython 中使用字符串? Py2 v Py3(字节串与 unicode)?
  • 不。试图开始,但发现字符串 + cython 令人生畏。

标签: python cython


【解决方案1】:

这不是 Cython 倾向于帮助解决的问题。它使用切片,最终的速度与纯 Python 大致相同(即实际上相当不错)。

使用 100 个字符的长字节字符串 (b'0'*100) 和 timeit 中的 10000 次迭代我得到:

  • 您编写的代码 - 0.37 秒
  • 您在 Cython 中编写但编译的代码 - 0.21 秒
  • 您的代码带有 cdef int i 行并在 Cython 中编译 - 0.20 秒(这是可重现的一个小改进。更长的字符串更显着)
  • 您的 cdef int i 和键入到 bytes text 的参数 - 0.28 秒(即更糟)。
  • 直接使用 Python C API 可以获得最佳速度(请参见下面的代码) - 0.11 秒。为方便起见,我选择主要在 Cython 中执行此操作(但自己调用 API 函数),但您可以直接在 C 中编写非常相似的代码,并进行更多的手动错误检查。假设您使用的是字节对象(即PyBytes 而不是PyString),我已经为 Python 3 API 编写了此代码,因此如果您使用的是 Python 2、Unicode 和 Python 3,则必须将其更改为小。

    from cpython cimport *
    cdef extern from "Python.h":
        # This isn't included in the cpython definitions
        # using PyObject* rather than object lets us control refcounting
        PyObject* Py_BuildValue(const char*,...) except NULL
    
    def split(text):
       cdef Py_ssize_t l,i
       cdef char* s
    
       # Cython automatically checks the return value and raises an error if 
       # these fail. This provides a type-check on text
       PyBytes_AsStringAndSize(text,&s,&l)
       output = PyList_New(l)
    
       for i in range(l):
           # PyList_SET_ITEM steals a reference
           # the casting is necessary to ensure that Cython doesn't
           # decref the result of Py_BuildValue
           PyList_SET_ITEM(output,i,
                           <object>Py_BuildValue('y#y#',s,i+1,s+i+1,l-(i+1)))
       return output
    
  • 如果您不想一直使用 C API,那么预分配列表 output = [None]*len(text) 并执行 for 循环而不是列表推导的版本比您的原始版本效率略高版本 - 0.18s

总而言之,只需在 Cython 中编译它就可以让您获得不错的加速(略低于 2 倍),并且设置 i 的类型会有所帮助。这就是常规使用 Cython 可以真正实现的全部目标。要获得全速,您基本上需要直接使用 Python C API。这让你的速度略低于 4 倍,我认为这是相当不错的。

【讨论】:

  • 哇。如果我在 python 2 中使用 unicode,我会将其更改为什么?只是用其他东西替换PyBytes_AsStringAndSize 吗?
  • Unicode 更复杂。我看不到AsStringAndSize 的直接替代品。我认为部分问题在于它存储的数据类型有多个选项(在 Python2 的编译时和 Python3 的运行时决定)。我认为您需要两次致电PyUnicode_GET_SIZEPyUnicode_AS_UNICODE。您还需要先检查类型,因为这些函数不这样做。最后,您需要将Py_BuildValue 中的“y#”更改为“u#”。由于运行时变量字符大小,Python3 更复杂。
  • 似乎我还必须将 cdef char* 替换为 cdef Py_UNICODE* 才能编译,但现在它可以工作了。太棒了!
猜你喜欢
  • 1970-01-01
  • 2011-06-21
  • 1970-01-01
  • 2020-04-14
  • 1970-01-01
  • 2019-04-16
  • 1970-01-01
  • 2017-05-26
  • 2021-03-19
相关资源
最近更新 更多