提高 Python 程序的运行速度

标签: python 程序 速度 | 发表时间:2011-10-05 00:44 | 作者:(author unknown) Ken
出处:http://simple-is-better.com/

尝试了一下用Python实现的K-Means Clustering算法,抽样了10000篇百科词条,分为1000个类,分词后词语总数为130000左右。如果把1000个类定义为1000个向量,每个向量的元素个数为130000,K-Means Clustering算法的第一步是初始化这1000个向量的值,如果每个向量元素的值用float型存储,则需要的内存为:

1000 * 130000 * sizeof(float)

约 520M 左右。

最初用Python的list存储,动态扩展列表大小,结果内存用到近3G还没初始化完成,只好赶紧kill掉了。

 改用 NumPy 存成固定数组的类别,发现内存使用量和计算结果基本一致,而且NumPy支持数组的数学计算,确实方便了不少,但即便如此,性能仍不够理想,K-Means算法第一次迭代花了一个小时还没完成。

怀疑K-Means算法的pearson距离计算时间较长(没有用profile工具论证过),用 Cython 重新改写了该算法,并尽可能缓存计算中间结果。第一轮迭代耗时1个多小时,算是有了进步。

 由于K-Means算法费CPU较多,改写了计算逻辑,用multiprocessing模块并行计算,在4核的CPU上速度提升了4倍,第一轮迭代花了约30分钟。但multiprocessing需要fork多个进程,每个进程的内存使用量均在600M上下想,4个进程占用了2.4G内存,代价有些大。而且计算结果在不同的进程间传递,性能开销也是存在的。 multiprocessing + 共享内存可以解决这个问题,但Python的数据结构不好表示。

继而想到写C的动态链接库,用Python的ctypes模块调用该动态链接库完成计算过程,C的动态链接库则创建系统线程,这样能有效躲过Python的GIL。问题是C代码有时候确实需要访问Python的数据对象,这只能通过Python的C扩展模块实现了,但C的扩展模块能访问ctypes的原始C指针吗?如果只能通过Python的C API访问,则GIL是绕不开的,我们的目标是尽可能少地锁住Python虚拟机。

解决办法是修改Python的ctypes源码,让它导出函数 addressof 的C API,这样在其他的C扩展模块里就能拿到ctypes的原始数据块指针。addressof的返回结果是一个long的PyObject包装对象,通过 PyLong_AsVoidPtr调用即可获取其值。

因此最后的解决办法是下载Python的源码,修改模块_ctypes的源码,通过Capsule导出C API。继而编写Python的C扩展模块,创建线程池,直接操作ctypes定义的数据内存。由于Python数据结构是非线程安全的,访问它们仍需获得GIL,但用到的可能性很小。程序的主体逻辑仍由Python代码构造,包括必要的ctypes数据结构。

代码改写后一次K-Means迭代不到一分钟就完成了,4颗CPU全跑满,内存仅占用600M左右,跟预期完全一致。

总结

  • Python大多数时间能如我们预期那样工作
  • 涉及到数值计算和海量循环,Python表现极其糟糕
  • Cython + NumPy 可解决部分计算问题和内存问题,但GIL无法避免
  • multiprocessing能解决SMP/GIL问题,但内存问题解决不了,也许共享内存+ctypes是个办法,没尝试过
  • ctypes + C的动态链接库创建系统线程能解决GIL和内存共享问题,但无法在C中操作Python对象
  • ctypes + Python C扩展意义不大,因为C扩展无法直接操作ctypes的数据指针
  • 改写后的ctypes + Python C扩展解决了性能和内存消耗问题
  • ctypes数据类型能被msgpack之类的工具快速序列化到磁盘,但恢复的时候只能恢复成list类型,这可能是美中不足的地方。

# 来源:冬天里的酒吧


在微博上关注: 新浪, 腾讯   投稿

最新招聘

更多>>

相关 [python 程序 速度] 推荐:

提高 Python 程序的运行速度

- Ken - python.cn(jobs, news)
尝试了一下用Python实现的K-Means Clustering算法,抽样了10000篇百科词条,分为1000个类,分词后词语总数为130000左右. 如果把1000个类定义为1000个向量,每个向量的元素个数为130000,K-Means Clustering算法的第一步是初始化这1000个向量的值,如果每个向量元素的值用float型存储,则需要的内存为:.

提高Python程序的运行速度

- Guancheng(冠诚) - 冬天里的酒吧
尝试了一下用Python实现的K-Means Clustering算法,抽样了10000篇百科词条,分为1000个类,分词后词语总数为130000左右. 如果把1000个类定义为1000个向量,每个向量的元素个数为130000,K-Means Clustering算法的第一步是初始化这1000个向量的值,如果每个向量元素的值用float型存储,则需要的内存为:.

Python程序员培训计划

- 敏 - 我的宝贝孙秀楠 ﹣C++, Lua, 大连,程序员
Python程序员很幸福,因为他们不需要像C#程序员那样东一榔头西一棒子的找资源学习. 把下面资源按顺序学完,代码都自己手敲过,应该就是初级以上水平了. 之所以选择py3k,是感觉新版本的生命力应该更长久一些. 学完tutorial,再看看这个Python竞赛,花个一两周时间搞定它. 最后看看这本书,了解一些有趣的话题.

Python程序的执行原理

- - 非技术 - ITeye博客
Python先把代码(.py文件)编译成字节码,交给字节码虚拟机,然后虚拟机一条一条执行字节码指令,从而完成程序的执行. 字节码在Python虚拟机程序里对应的是PyCodeObject对象. .pyc文件是字节码在磁盘上的表现形式. PyCodeObject对象的创建时机是模块加载的时候,即import.

探究如何给Python程序做hotfix

- - IT瘾-tuicool
使用Python来写服务器端程序,很大的一个优势就是可以进行热更新,即在不停机的情况下,使改动后的程序生效. 在开发阶段,这个功能可以大大提高开发效率(写代码–启动服务器–看效果–改代码–hotfix–看效果–提交~);而在生产环境中,可以以最小的代价(不停机)修复线上的bug. 我在项目中使用hotfix功能很长世间了,大概了解它是利用了Python的import/reload功能,但是并没有去自己研究过.

每个程序员都应该学习使用Python或Ruby

- Kings - 开源中国社区最新新闻
本文是从 Why every programmer should learn Python or Ruby 这篇文章翻译而来. 如 果你是个学生,你应该会C,C++和Java. 还会一些VB,或C#/.NET. 多少你还可能开发过一些Web网页,你知道一些HTML,CSS和 JavaScript知识.

Python程序语言快速上手教程

- - SEM WATCH
本文是面向SEO人群的Python程序语言入门教程,也适用于其他没有程序基础但想学习些程序,以解决简单的实际应用需求的人群. 在后面会尽量用最基础的角度来介绍这门语言. 本来打算从网上找一篇入门教程,但因为Python很少是程序员的第一次接触程序所学的语言,所以网上现有的教程多不是很基础,还是决定自己写下这些.

全面解读python web 程序的9种部署方式

- - 鲁塔弗的博客
python有很多web 开发框架,代码写完了,部署上线是个大事,通常来说,web应用一般是三层结构. 主流的web server 一个巴掌就能数出来,apache,lighttpd,nginx,iis. application,中文名叫做应用服务,就是你基于某个web framework写的应用代码.

Python 程序员应该知道的 10 个库

- - 博客 - 伯乐在线
Python是优雅的,使用这些库可以使你的代码更简洁,并保持持久性. 抛弃 optparse和 argparse吧,使用 docstrings来构建优雅的,可读性强的,并且复杂(如果你需要的话)的命令行界面. IMO2013年创建的最好的库. Requests,或称为人类使用的HTTP,是一个处理HTTP请求更为pythonic 的方法,比 urllib2更更更好用.

让你的python程序同时兼容python2和python3

- - python.cn(jobs, news)
python邮件列表里有人发表言论说“python3在10内都无法普及”. 在我看来这样的观点有些过于悲观,python3和python2虽然不兼容,但他们之间差别并没很多人想像的那么大. 你只需要对自己的代码稍微做些修改就可以很好的同时支持python2和python3的. 下面我将简要的介绍一下如何让自己的python代码如何同时支持python2和python3.