加速Python

这实际上是两个问题,但它们非常相似,为了保持简单,我想我只是把它们放在一起:

  • 首先 :给定一个已经build立的python项目,有什么体面的方法可以加速超越简单的代码内优化?

  • 其次 :在python中从头开始编写程序时,有什么方法可以大大提高性能?

对于第一个问题,想象一下你是一个写得很好的项目,你需要提高性能,但是你通过重构/优化似乎无法获得太多收获。 在这种情况下,你会怎么做才能加快速度呢?

关于“其次:在python中从头开始编写程序时,有什么方法可以大大提高性能?

记住jackson的优化规则:

  • 规则1:不要这样做。
  • 规则2(仅适用于专家):不要这样做。

Knuth规则:

  • “不成熟的优化是万恶之源”。

“优化通用规则 ”中更有用的规则。

  1. 不要随意优化。 首先说得对。 然后快点。 优化错误的程序仍然是错误的。

  2. 记住80/20规则。

  3. 总是运行“之前”和“之后”的基准。 否则,你不知道你是否find了80%。

  4. 使用正确的algorithm和数据结构。 这条规则应该是第一位的。 algorithm和数据结构并不重要。

底线

你不能阻止或避免“优化这个程序”的努力。 这是工作的一部分。 你必须做好计划,仔细地做,就像devise,编码和testing活动一样。

而不是只是踢C,我会build议:

让你的代码数。 用更less的行数执行更多的事情:

  • 将algorithm更改为更快的algorithm。 在许多情况下,不需要看起来更快。
  • 使用碰巧写在C中的Python原语。有些东西会强制解释器调度,其中一些不会。 后者是可取的
  • 谨防首先构build一个大数据结构的代码,然后是它的代价。 想想范围和xrange之间的区别。 一般来说,通常值得考虑程序的内存使用情况。 使用生成器有时会将O(n)内存使用降至O(1)。
  • Python通常是非优化的。 在循环中提升不变代码,在紧密循环中尽可能消除常见的子expression式。
  • 如果有些东西是昂贵的,然后预先计算或记忆它。 正则expression式可以编译为例子。
  • 需要紧缩数字? 你可能想检查一下numpy
  • 许多python程序很慢,因为它们受磁盘I / O或数据库访问的约束。 确保在等待数据到达而不仅仅是阻止时有一些值得做的事情。 武器可能是像Twisted框架。
  • 请注意,许多关键的数据处理库都有C版本,无论是XML,JSON还是其他。 它们通常比Python解释器快得多。

如果以上所有内容都不能用于分析和测量的代码,那么就开始考虑C-rewritepath。

通常的嫌疑人 – 简介,find最昂贵的线路,找出它在做什么,解决它。 如果以前没有做过太多的分析,可能会有一些大的二次方形循环或string重复隐藏在看起来无害的expression式之后。

在Python中,我发现非常明显的两个最常见的原因是string连接和生成器。 由于Python的string是不可变的,所以像这样做:

 result = u"" for item in my_list: result += unicode (item) 

将每个迭代复制整个string两次。 这已经被覆盖了,解决方法是使用"".join

 result = "".join (unicode (item) for item in my_list) 

发电机是另一个罪魁祸首。 它们非常易于使用,并且可以极大地简化一些任务,但是应用不佳的生成器会比简单地将项目附加到列表并返回列表慢得多。

最后, 不要害怕重写C中的位! Python作为一种dynamic的高级语言,根本无法匹配C的速度。 如果有一个函数不能在Python中进行优化,请考虑将其提取到扩展模块。

我最喜欢的技术是维护一个模块的Python和C版本。 Python版本的编写尽可能清晰明了 – 任何错误都应该易于诊断和修复。 在这个模块上写下你的testing。 然后编写C版本,并testing它。 它的行为在任何情况下都应该与Python实现的行为相同 – 如果它们不同,应该很容易找出哪些是错误的,并纠正问题。

首先想到的是psyco 。 它只在x86上运行,暂时。

然后, 不断的绑定 。 也就是说,使所有全局引用(和global.attrglobal.attr.attr …)成为函数和方法内部的本地名称。 这并不总是成功的,但一般来说它是有效的。 这可以通过手工完成,但显然很乏味。

你说的除了代码优化之外,所以我不会深入研究这个问题,但是对于典型的错误( for i in range(10000000)想到for i in range(10000000) ),人们会这样做。

Cython和pyrex可以用来使用类似python的语法生成c代码。 对于合适的项目,Psyco也是非常棒的(有时你不会注意到速度的提升,有时甚至会达到50倍)。 我仍然认为最好的方法是分析你的代码(cProfile等),然后将代码作为python的c函数的瓶颈。

我很惊讶没有人提到ShedSkin: http : //code.google.com/p/shedskin/ ,它自动将你的python程序转换为C ++,并且在一些基准testing中,速度比psyco有更好的改进。

更简单的传奇故事: http : //pyinsci.blogspot.com/2006/12/trying-out-latest-release-of-shedskin.html

虽然有限制,请参阅: http : //tinyurl.com/shedskin-limitations

我希望你已经阅读: http : //wiki.python.org/moin/PythonSpeed/PerformanceTips

恢复已有的通常3个原则:

  • 编写可以在更好的字节码中转换的代码,就像使用本地代码一样,避免不必要的查找/调用,使用惯用的结构(如果有自然的语法来使用它,通常使用更快的方法,例如:不要做: some_dict.keys()“,做”some_dict中的键“)
  • 不pipe用C写成什么,都要快得多,滥用你可用的C函数/模块
  • 如有疑问,请input时间,档案

通过Python分析器运行你的应用程序。 发现一个严重的瓶颈。 重写C.重复中的瓶颈。

人们已经给出了一些很好的build议,但是你必须知道,当需要高性能时,python模型是:踢到c。 像psyco这样的努力可能在未来会有所帮助,但是python并不是一门快速的语言,而且它的devise也并非如此。 很less有语言能够很好地完成dynamic的function,而且还能生成非常快速的代码。 至less在可预见的将来(和一些反对快速编译的devise工作)将是如此。

所以,如果你真的发现自己在这个绑定中,最好的办法就是隔离你的系统中不能接受的(好)python缓慢的部分,并围绕这个想法来devise你将用C重写那些位。对不起。 良好的devise可以帮助减轻痛苦。 尽可能在python中初始化它,然后你很容易在你的c上得到一个理智的检查。

毕竟,这对于像numpy这样的东西来说已经足够好了。 我不能强调多less好devise会帮助你。 如果你只是反复地捅你的Python位,并用C代替最慢的,那么最终可能会是一团糟。 仔细想想C位需要什么位置,以及如何最小化和合理地封装。

这不一定会加快你的代码,但如果你想避免减慢你的代码速度,那么在使用Python进行编程时,这是非常重要的知识。 “全球解释器locking”(GIL)有可能大大降低你的multithreading程序的速度,如果它的行为不被理解(是的,这个位我…我有一个不错的4处理器,不会一次使用超过1.2个处理器)。 有一篇介绍文章,有一些链接让你开始使用SmoothSpan 。

只是使用psyco的一个注意事项:在某些情况下,它实际上可能会产生较慢的运行时间。 特别是当试图用C编写的代码使用psyco时,我不记得我读过这篇文章,但是专门提到了map()reduce()函数。 幸运的是,你可以告诉psyco不要处理指定的function和/或模块。

这是我试图遵循的程序:

  • importpsyco; psyco.full()
  • 如果速度不够快,请通过分析器运行代码,查看瓶颈位置。 (这一步禁用psyco!)
  • 尝试去做其他人提到的事情,尽可能快地获得这些瓶颈。
    • 像[str(x)for x in l]或[x.strip()for x]这样的东西比map(str,x)或map(str.strip,x)要慢得多。
  • 在此之后,如果我仍然需要更多的速度,那么让PyRex启动和运行真的很容易。 我首先复制一段python代码,直接把它放在pyrex代码中,看看会发生什么。 然后我把它旋转,直到它变得越来越快。

通过使用内置的Python调用,用隐式algorithm代替在Python中用longhand写出的显式algorithm,通常可以实现接近C的速度(对于任何使用Python的项目来说足够近)。 这是可行的,因为大多数Python内置插件都是用C编写的。 那么,在CPython当然;-) https://www.python.org/doc/essays/list2str/

如何改进Python代码的规范参考在这里: PerformanceTips 。 除非你真的需要,否则我build议不要在C中进行优化。 对于大多数应用程序,您可以按照该链接中发布的规则获得所需的性能。

如果使用psyco,我build议使用psyco.profile()而不是psyco.full() 。 对于一个更大的项目,它会更聪明的function得到优化,并使用更less的内存。

我也build议看一下迭代器和生成器。 如果您的应用程序正在使用大型数据集,这将为您节省许多容器的副本。

除了(很棒的) psyco和(漂亮的) 大棚皮 ,我build议你尝试一下cython一个很棒的pyrex叉子。

或者,如果你不急,我build议等待。 较新的python虚拟机即将问世 ,而空载吞入将成为主stream。

在提出这个问题之后,介绍了几种加速Python代码的方法:

  • Pypy有一个JIT编译器,这使得CPU绑定代码更快。
  • Pypy是用Rpython编写的,Python是Python的一个子集,编译为本地代码,利用LLVM工具链。

对于一个既定的项目,我觉得主要的性能收益将来自于尽可能地使用python内部库。

一些技巧在这里: http : //blog.hackerearth.com/faster-python-code