译者:

原文:

The GITS Blog » Extending Python with C: A case study

- 实例分析: 使用 C 扩展 Python

本周热评

其它收藏服务:

金牌译作 实例分析: 使用 C 扩展 Python

2468个读者 kyhpuddin... @ yeeyan.com 2008年02月08日 双语对照  原文 字体大小

简介

实例分析: 使用 C 扩展 Python

应用 C 扩展 --- 将近百倍的性能提升

最近我写过一篇关于在 Python 中实现模糊子串匹配的算法的文章. 我正在开发的一个软件将会用到这个功能.

但当我开始在一些测试用例中使用 fuzzy_substring 函数时, 它慢得不可接受. 对一个大却合理的测试集以及约 1000 个检索词, 函数的处理事件长达约 30 秒. 它是要负责响应用户查询的, 30 秒的等待时间实在太长了.

首次尝试

为了让它跑得更快, 我首先尝试了 psyco. 仅需在脚本开头加入一句 psyco.full(), 以上测试所花的时间缩短到了 11 秒. 没有任何副作用, 却带来了 3 倍的速度提升, 真棒!

Psyco 确实很好用, 但要运行 11 秒还是太慢了. 所以, 我忍痛放弃, 转而试用 C 扩展模块来实现这个函数.

Python 的 C 扩展

我以前从未写过纯 C 扩展模块, 况且我已经有约 10 年未接触过 C 语言了. 所以看完官方的扩展和嵌入文档以及 Michael Hudson 的线上教程后, 我感到有些惶恐.

实际上这却意想不到地简单, 我只是将文档中的样例代码和 setup.py 复制过来, 填上我的功能代码, 运行 setup.py, 一切就都弄好了. subdist.pyd 文件就这样闪亮登场了! 由于我已经用 python 实现过这个函数, 我只用了不到一个小时就完成了相应的 C 代码, 而且我可以用相同的单元测试去验证它.

更新: 我为这个扩展模块创建了一个页面, 包含了源代码和目标二进制模块.

基准测试

我对 C 扩展模块和对应的纯 Python 模块 (包括使用了和没有使用 psyco 的版本). 测试中, 我使用了 Python 2.5 README 文件中的 1000 个不同的词作为检索词, 将同一文档的第 100 到 200 行作为待检索集. 以下代码读入待检索集

text = unicode(open("/python25/readme.txt").read())
sentences = text.splitlines()[100:200]
words = list(set(text.split()))[:1000]

接着, 对测试中的每个 ``语句'' 进行模糊子串匹配

def time_func(func, words, sentences):
start = time.time()
for sentence in sentences:
for word in words:
func(word, sentence)
return time.time() - start

加速成果

将每种实现的函数运行十次, 以下表格列出他们的最快, 最慢以及平均 (median) 执行时间:



Low Median High
Python 53.6410 s 54.6560 s 55.2190 s
Python (psyco) 17.6100 s 17.7960 s 18.0620 s
C 0.7030 s 0.7190 s 0.7340 s

加速比

Python (psyco) to Python: 0.3256
C to Python (psyco): 0.0404
C to Python: 0.0132

可见, C 扩展比纯 Python 函数实现快了近 100 倍, 比使用 psyco 的 Python 实现也快了超过 20 倍. 这是仅仅一小时工作的结果, 着实不坏!

实际上我还能用一些小技巧让性能再提高个 10% 到 30%. (比如不计算左下角和右上角), 但是就我目前的目的而言, 它已经足够快了. 如果我真要再在这上面压榨点性能, 我会弄点艰涩难懂的优化的.

总结

编译一个 Python 的 C 扩展比我想象的要简单得多. 纯 C 代码特别适合类似这样的场合, 你可以不管 Python 对象而直接去操纵 C 数据结构. 通过将它换成 C 实现, 我的这个模糊字符串匹配算法从一个仅是有趣的玩意变为了一个真正实用的东西.

在性能瓶颈处使用这样的方法就能得到如此大的性能提升, 实在是太神奇了. 而且我依然可以用纯 Python 实现我其他 99% 的程序代码.

下载

点击此处下载, 所有的源代码和二进制模块以 MIT 形式的许可发布.

使用说明

from subdist import substring
print substring(u"needle", u"Find the needle in the haystack")

注意: 函数只接受 Unicode 编码的字符串. 当传进非 Unicode 字符串时它会返回一个错误.

本文版权所有,未经许可,请勿转载
内容合作请 联系我们

译作评分
0.0
请给出您的评分  提交

更多关于 Python 编程 巨蟒 extension Development 的翻译文章

1条评论

添加评论

阅读
发现
翻译
合作媒体

Copyright © 2009 yeeyan.com All rights reserved. 京ICP证080457号