简体   繁体   English

Cythonize字符串的所有拆分列表

[英]Cythonize list of all splits of a string

I'm trying to speed up a piece of code that generates all possible splits of a string. 我正在尝试加速一段代码,生成所有可能的字符串拆分。

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

The code for this in python is very simple: python中的代码非常简单:

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

Is there a way to speed this up via cython or some other means? 有没有办法通过cython或其他方式加快速度? For context, the greater purpose of this code is to find the split of a string with the highest probability. 对于上下文,此代码的更大目的是找到具有最高概率的字符串的拆分。

This isn't the sort of problem that Cython tends to help with much. 这不是Cython倾向于帮助的问题。 It's using slicing, which ends up largely the same speed as pure Python (ie actually pretty good). 它使用切片,最终与纯Python的速度大致相同(即实际上相当不错)。

Using a 100 character long byte string ( b'0'*100 ) and 10000 iterations in timeit I get: 使用100个字符的长字节串( b'0'*100 )和10000次迭代timeit我得到:

  • Your code as written - 0.37s 您编写的代码 - 0.37s
  • Your code as written but compiled in Cython - 0.21s 您编写的代码,但在Cython中编译 - 0.21s
  • Your code with the line cdef int i and compiled in Cython - 0.20s (this is reproducably a small improvement. It's more significant with longer strings) 您的代码使用行cdef int i并在Cython中编译 - 0.20s(这是可重复的一个小改进。对于更长的字符串,它更重要)
  • Your cdef int i and the parameter typed to bytes text - 0.28s (ie worse). 你的cdef int i和键入bytes text的参数bytes text - 0.28s(即更糟)。
  • Best speed is got by using the Python C API directly (see code below) - 0.11s. 通过直接使用Python C API获得最佳速度(参见下面的代码) - 0.11s。 I've chosen to do this mostly in Cython (but calling the API functions myself) for convenience, but you could write very similar code in C directly with a little more manual error checking. 为了方便起见,我选择在Cython(但是自己调用API函数)中这样做,但你可以直接在C中编写非常相似的代码,并进行更多的手动错误检查。 I've written this for the Python 3 API assuming you're using bytes objects (ie PyBytes instead of PyString ) so if you're using Python 2, or Unicode and Python 3 you'll have to change it a little. 我写这一个为Python 3 API假设你使用的字节对象(即PyBytes代替PyString ),所以如果你正在使用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 
  • If you don't want to go all the way with using the C API then a version that preallocates the list output = [None]*len(text) and does a for-loop rather than a list comprehension is marginally more efficient than your original version - 0.18s 如果你不想一直使用C API那么预先分配列表output = [None]*len(text)的版本output = [None]*len(text)并执行for循环而不是列表理解比你的更有效率原始版本 - 0.18s

In summary, just compiling it in Cython gives you a decent speed up (a bit less than 2x) and setting the type of i helps a little. 总之,只需在Cython中编译它就可以获得相当快的速度(略低于2倍)并且设置i的类型有点帮助。 This is all you can really achieve with Cython conventionally. 这是您通过常规方式实现的所有功能。 To get full speed you basically need to resort to using the Python C API directly. 要获得全速,您基本上需要直接使用Python C API。 That gets you a little under a 4x speed up which I think is pretty decent. 这让你速度提高了4倍,我认为这相当不错。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM