Cython中的字符串操作

its*_*dok 28 python string cython

我有一些代码可以执行一些非常耗费CPU的字符串操作,我一直在寻找提高性能的方法.

(编辑:我正在做的事情就像找到最常见的子串,运行大量正则表达式,可能更好地表达为c中的状态机,从HTML中剥离注释,这样的东西.)

我目前正在考虑将一些代码移植到Cython之后听到很多关于它的好东西.然而,似乎Cython的主要焦点是数值计算,并且使用字符串几乎没有记录.

Unicode也可能是一个大问题.

我的问题是:

  1. 我是否应该为Cython打扰字符串?有没有人在cython中有这种类型的处理经验,可以分享?
  2. 我在Cython文档中遗漏了什么吗?有没有人知道在Cython中使用字符串的教程/参考/文档?

Kyl*_*tan 11

我投了'profile it'的答案,但想补充一点:在可能的情况下,你可以做的最好的优化是使用Python标准库或内置函数来执行你想要的任务.这些通常在C中实现,并且将提供与任何扩展大致相当的性能,包括用Cython编写的扩展.如果你的算法在Python中逐字符循环执行,那么这些应该是第一个要做的事情,如果可能的话.

但是,如果您的算法无法根据内置函数或其他现有标准库进行重写,那么Cython似乎是一种合理的方法.它只是将伪Python编译为本机代码,并且与任何其他操作一样适合字符串操作.但是我不相信如果你只是提供惯用的Python代码,你会看到使用Cython带来的巨大好处.如果您能够在C中重写每个算法的部分或全部,那么最大的好处就会出现,这样低级操作就不会在Python/C障碍中不断地转换变量.

最后,Unicode - 你暗示它可能是'一个大问题',但没有说明你如何使用它.Cython可能会产生C代码,调用处理Unicode的相关Python API,因此功能不太可能受到限制.但是,在C中处理Unicode字符串并不重要,可能意味着在C中重写某些算法以获得更好性能的想法并不值得.许多经典的字符串算法根本不适用于许多Unicode编码,这些编码不是传统意义上的"字符串",即每个字符有1个单位的存储空间.


its*_*dok 9

为了完整起见,我最终做的只是在C中编写(某些)字符串操作代码.

事实证明,开始编写python的c扩展是非常容易的.Unicode字符串只是Py_UNICODE的数组,取决于python构建,它是一个int或short.

我得到了x20改进转换代码

s = re.sub(r' +', ' ', s)
Run Code Online (Sandbox Code Playgroud)

到c.我使用更复杂的regexp得到了类似的改进,但c代码变得非常复杂.

总的来说,重写后我的吞吐量上升了20%.我现在正在寻找更多要改写的东西......


Joh*_*hin 8

"Ridiculously easy"是一个非常相对的术语."入门"就是这样.在C中编写健壮的扩展需要非常小心地注意引用计数,内存分配/释放和错误处理等事情.Cython为你做了很多.

Cython中的非unicode字符串是一个Python str对象,或者它是一个char数组,如在C中.您认为您需要什么特定于Cython的文档?

我建议您自己尝试Cython.但在您这样做之前,我强烈建议您检查Python代码是否效率低下.有时你可以轻松地获得大幅加速.

例如,压缩空格字符的运行...使用

re.sub(' +', ' ', s) # one space in pattern
Run Code Online (Sandbox Code Playgroud)

意味着在一个运行长度为1的大概并非罕见的情况下,它将用空格替换空格.如果所有运行都具有长度1,则它将创建一个新的替换字符串,因为它可以轻松地递增(或不递减或其他)输入字符串的引用计数并将其传回.

re.sub('  +', ' ', s) # two spaces in pattern
Run Code Online (Sandbox Code Playgroud)

产生完全相同的结果,并可能运行得更快...让我们看看:

所有运行长度为1:它以3.4倍的速度运行.未显示:输入字符串越长,越好.

\python26\python -mtimeit -s"s='now is the winter of our discontent'; import re; x = re.compile(' +').sub" "x(' ', s)"
100000 loops, best of 3: 8.26 usec per loop

\python26\python -mtimeit -s"s='now is the winter of our discontent'; import re; x = re.compile('  +').sub" "x(' ', s)"
100000 loops, best of 3: 2.41 usec per loop
Run Code Online (Sandbox Code Playgroud)

一次运行的长度为2,速度比为2.5.所有运行长度为2,速度比为1.2.考虑到所有因素,投资1次击键并不是一个糟糕的回报.


End*_*age 6

我最近被介绍给Cython,并且已经成功地将大型C和C++库包装在重要项目中.实际上,一些生成的Python扩展已经在我们的生产环境中运行.所以,首先,Cython,imo,绝对是一个不错的选择.

话虽这么说,您应该考虑是否真的想在Cython中编写所有代码,或者是否要编写C/C++代码并简单地从Cython中访问这些函数.显然,这将部分取决于您对C和/或C++的舒适程度.

当你使用Strings时,你可以通过使用std::stringC++而不是使用C++来简化你的生活char*.它可以非常容易地导入到cython中,from libcpp.string cimport string然后可以通过标准的cython使用字符串类型声明变量cdef string ...


小智 5

这是一个非常有趣的问题.Cython的核心是一个将python与C数据类型集成的工具.它没有提供任何功能来协助处理字符串,可能是因为没有像特定Numpy功能那样多的需求.

话虽如此,您可以使用Cython与现有的C/C++库进行交互,这些库旨在处理您描述的问题类型.例如,对于处理HTML/XML,您可能需要查看libxml.然而,当然有(现在)现成的python绑定已经可用.我已经广泛使用lxml来处理HTML,它完成了我所需要的所有工作并快速完成,而且它可以很好地处理unicode.

在你的情况下,我会想象lxml和定制C函数的组合将是最好的.例如,您可以"轻松"创建一个快速函数来查找C中最长的子字符串,因为这可以在字节级别完成(回想一下,C中的字符串只是一个char*,它是一个字节数组).然后你可以将它们映射回python(Cython将使你真正变得容易)并继续在unicode抽象的天堂:).当然不是微不足道的,但如果您的应用程序的性能依赖于它,则可能值得付出努力.

然后在C/C++中使用unicode当然有很好的(尽管是非平凡的)方法.Evan Jones 撰写的这篇文章可以帮助您确定是否值得付出努力.