Python 性能优化工具

Python 性能优化除了改进算法,选用合适的数据结构之外,还有几种关键的技术,比如将关键 python 代码部分重写成 C 扩展模块,或者选用在性能上更为优化的解释器等,这些在本文中统称为优化工具。python 有很多自带的优化工具,如 Psyco,Pypy,Cython,Pyrex 等,这些优化工具各有千秋,本节选择几种进行介绍。

Psyco

psyco 是一个 just-in-time 的编译器,它能够在不改变源代码的情况下提高一定的性能,Psyco 将操作编译成有点优化的机器码,其操作分成三个不同的级别,有”运行时”、”编译时”和”虚拟时”变量。并根据需要提高和降低变量的级别。运行时变量只是常规 Python 解释器处理的原始字节码和对象结构。一旦 Psyco 将操作编译成机器码,那么编译时变量就会在机器寄存器和可直接访问的内存位置中表示。同时 python 能高速缓存已编译的机器码以备今后重用,这样能节省一点时间。但 Psyco 也有其缺点,其本身运行所占内存较大。目前 psyco 已经不在 python2.7 中支持,而且不再提供维护和更新了,对其感兴趣的可以参考 http://psyco.sourceforge.net/

Pypy

PyPy 表示 “用 Python 实现的 Python”,但实际上它是使用一个称为 RPython 的 Python 子集实现的,能够将 Python 代码转成 C, .NET, Java 等语言和平台的代码。PyPy 集成了一种即时 (JIT) 编译器。和许多编译器,解释器不同,它不关心 Python 代码的词法分析和语法树。 因为它是用 Python 语言写的,所以它直接利用 Python 语言的 Code Object.。 Code Object 是 Python 字节码的表示,也就是说, PyPy 直接分析 Python 代码所对应的字节码 ,,这些字节码即不是以字符形式也不是以某种二进制格式保存在文件中, 而在 Python 运行环境中。目前版本是 1.8. 支持不同的平台安装,windows 上安装 Pypy 需要先下载https://bitbucket.org/pypy/pypy/downloads/pypy-1.8-win32.zip,然后解压到相关的目录,并将解压后的路径添加到环境变量 path 中即可。在命令行运行 pypy,如果出现如下错误:”没有找到 MSVCR100.dll, 因此这个应用程序未能启动,重新安装应用程序可能会修复此问题”,则还需要在微软的官网上下载 VS 2010 runtime libraries 解决该问题。具体地址为http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=5555

安装成功后在命令行里运行 pypy,输出结果如下:

  1. C:\Documents and Settings\Administrator>pypy  
  2.  Python 2.7.2 (0e28b379d8b3, Feb 09 201218:31:47)  
  3.  [PyPy 1.8.0 with MSC v.1500 32 bit] on win32  
  4.  Type "help""copyright""credits" or "license" for more information.  
  5.  And now for something completely different: ``PyPy is vast, and contains  
  6.  multitudes'' 
  7.  >>>> 

以清单5 的循环为例子,使用 python 和 pypy 分别运行,得到的运行结果分别如下:

  1. C:\Documents and Settings\Administrator\ 桌面 \doc\python>pypy loop.py  
  2.  total run time:  
  3.  8.42199993134 
  4. C:\Documents and Settings\Administrator\ 桌面 \doc\python>python loop.py  
  5.  total run time:  
  6.  106.391000032 

可见使用 pypy 来编译和运行程序,其效率大大的提高。

Cython

Cython 是用 python 实现的一种语言,可以用来写 python 扩展,用它写出来的库都可以通过 import 来载入,性能上比 python 的快。cython 里可以载入 python 扩展 ( 比如 import math),也可以载入 c 的库的头文件 ( 比如 :cdef extern from “math.h”),另外也可以用它来写 python 代码。将关键部分重写成 C 扩展模块

Linux Cpython 的安装:

第一步:下载

  1. [root@v5254085f259 cpython]# wget -N http://cython.org/release/Cython-0.15.1.zip  
  2.  --2012-04-16 22:08:35-- http://cython.org/release/Cython-0.15.1.zip 
  3.  Resolving cython.org... 128.208.160.197 
  4.  Connecting to cython.org|128.208.160.197|:80... connected.  
  5.  HTTP request sent, awaiting response... 200 OK  
  6.  Length: 2200299 (2.1M) [application/zip]  
  7.  Saving to: `Cython-0.15.1.zip'  
  8. 100%[======================================>] 2,200,299 1.96M/s in 1.1s 
  9. 2012-04-16 22:08:37 (1.96 MB/s) - `Cython-0.15.1.zip' saved [2200299/2200299

第二步:解压

  1. [root@v5254085f259 cpython]# unzip -o Cython-0.15.1.zip 

第三步:安装

  1. python setup.py install 

安装完成后直接输入 cython,如果出现如下内容则表明安装成功。

  1. [root@v5254085f259 Cython-0.15.1]# cython  
  2. Cython (http://cython.org) is a compiler for code written in the  
  3. Cython language.  Cython is based on Pyrex by Greg Ewing.  
  4.    
  5. Usage: cython [options] sourcefile.{pyx,py} ...  
  6.    
  7. Options:  
  8.  -V, --version                  Display version number of cython compiler  
  9.  -l, --create-listing           Write error messages to a listing file  
  10.  -I, --include-dir <directory>  Search for include files in named directory  
  11.                                 (multiple include directories are allowed).  
  12.  -o, --output-file <filename>   Specify name of generated C file  
  13.  -t, --timestamps               Only compile newer source files  
  14.  -f, --force                    Compile all source files (overrides implied -t)  
  15.  -q, --quiet                    Don't print module names in recursive mode  
  16.  -v, --verbose                  Be verbose, print file names on multiple compil ation  
  17.  -p, --embed-positions          If specified, the positions in Cython files of each  
  18.  function definition is embedded in its docstring.  
  19.  --cleanup <level>  
  20.  Release interned objects on python exit, for memory debugging.  
  21.    Level indicates aggressiveness, default 0 releases nothing.  
  22.  -w, --working <directory>  
  23.  Sets the working directory for Cython (the directory modules are searched from)  
  24.  --gdb Output debug information for cygdb  
  25.  -D, --no-docstrings  
  26.              Strip docstrings from the compiled module.  
  27.  -a, --annotate  
  28.              Produce a colorized HTML version of the source.  
  29.  --line-directives  
  30.              Produce #line directives pointing to the .pyx source  
  31.  --cplus  
  32.              Output a C++ rather than C file.  
  33.  --embed[=<method_name>]  
  34.              Generate a main() function that embeds the Python interpreter.  
  35.  -2          Compile based on Python-2 syntax and code seman tics.  
  36.  -3          Compile based on Python-3 syntax and code seman tics.  
  37.  --fast-fail     Abort the compilation on the first error  
  38.  --warning-error, -Werror       Make all warnings into errors  
  39.  --warning-extra, -Wextra       Enable extra warnings  
  40.  -X, --directive <name>=<value>  
  41.  [,<name=value,...] Overrides a compiler directive 

其他平台上的安装可以参考文档:http://docs.cython.org/src/quickstart/install.html

Cython 代码与 python 不同,必须先编译,编译一般需要经过两个阶段,将 pyx 文件编译为 .c 文件,再将 .c 文件编译为 .so 文件。编译有多种方法:

通过命令行编译:

假设有如下测试代码,使用命令行编译为 .c 文件。

  1. def sum(int a,int b):  
  2.         print a+b  
  3.    
  4.  [root@v5254085f259 test]# cython sum.pyx  
  5.  [root@v5254085f259 test]# ls  
  6.  total 76 
  7.  4 drwxr-xr-x 2 root root  4096 Apr 17 02:45 .  
  8.  4 drwxr-xr-x 4 root root  4096 Apr 16 22:20 ..  
  9.  4 -rw-r--r-- 1 root root    35 Apr 17 02:45 1 
  10.  60 -rw-r--r-- 1 root root 55169 Apr 17 02:45 sum.c  
  11.  4 -rw-r--r-- 1 root root    35 Apr 17 02:45 sum.pyx 

在 linux 上利用 gcc 编译为 .so 文件:

  1. [root@v5254085f259 test]# gcc -shared -pthread -fPIC -fwrapv -O2  
  2. -Wall -fno-strict-aliasing -I/usr/include/python2.4 -o sum.so sum.c  
  3. [root@v5254085f259 test]# ls  
  4. total 96 
  5. 4 drwxr-xr-x 2 root root  4096 Apr 17 02:47 .  
  6. 4 drwxr-xr-x 4 root root  4096 Apr 16 22:20 ..  
  7. 4 -rw-r--r-- 1 root root    35 Apr 17 02:45 1 
  8. 60 -rw-r--r-- 1 root root 55169 Apr 17 02:45 sum.c  
  9. 4 -rw-r--r-- 1 root root    35 Apr 17 02:45 sum.pyx  
  10. 20 -rwxr-xr-x 1 root root 20307 Apr 17 02:47 sum.so 

使用 distutils 编译

建立一个 setup.py 的脚本:

  1. from distutils.core import setup  
  2.  from distutils.extension import Extension  
  3.  from Cython.Distutils import build_ext  
  4.    
  5.  ext_modules = [Extension("sum", ["sum.pyx"])]  
  6.    
  7.  setup(  
  8.     name = 'sum app',  
  9.     cmdclass = {'build_ext': build_ext},  
  10.     ext_modules = ext_modules  
  11.  )  
  12.    
  13.  [root@v5254085f259 test]#  python setup.py build_ext --inplace  
  14.  running build_ext  
  15.  cythoning sum.pyx to sum.c  
  16.  building 'sum' extension  
  17.  gcc -pthread -fno-strict-aliasing -fPIC -g -O2 -DNDEBUG -g -fwrapv -O3  
  18.  -Wall -Wstrict-prototypes -fPIC -I/opt/ActivePython-2.7/include/python2.7 
  19.   -c sum.c -o build/temp.linux-x86_64-2.7/sum.o  
  20.  gcc -pthread -shared build/temp.linux-x86_64-2.7/sum.o  
  21.  -o /root/cpython/test/sum.so 

编译完成之后可以导入到 python 中使用:

  1. [root@v5254085f259 test]# python  
  2. ActivePython 2.7.2.5 (ActiveState Software Inc.) based on  
  3. Python 2.7.2 (default, Jun 24 201111:24:26)  
  4. [GCC 4.0.2 20051125 (Red Hat 4.0.2-8)] on linux2  
  5. Type "help""copyright""credits" or "license" for more information.  
  6. >>> import pyximport; pyximport.install()  
  7. >>> import sum  
  8. >>> sum.sum(1,3

下面来进行一个简单的性能比较:

清单 9. Cython 测试代码

  1. from time import time  
  2.  def test(int n):  
  3.  cdef int a =0 
  4.  cdef int i  
  5.  for i in xrange(n):  
  6.  a+= i  
  7.  return a  
  8. t = time()  
  9.  test(10000000)  
  10.  print "total run time:" 
  11.  print time()-t 

测试结果:

  1. [GCC 4.0.2 20051125 (Red Hat 4.0.2-8)] on linux2  
  2.  Type "help""copyright""credits" or "license" for more information.  
  3.  >>> import pyximport; pyximport.install()  
  4.  >>> import ctest  
  5.  total run time:  
  6.  0.00714015960693 

清单 10. Python 测试代码

  1. from time import time  
  2.  def test(n):  
  3.  a =0;  
  4.  for i in xrange(n):  
  5.  a+= i  
  6.  return a  
  7. t = time()  
  8.  test(10000000)  
  9.  print "total run time:" 
  10.  print time()-t  
  11. [root@v5254085f259 test]# python test.py  
  12.  total run time:  
  13.  0.971596002579 

从上述对比可以看到使用 Cython 的速度提高了将近 100 多倍。

总结

本文初步探讨了 python 常见的性能优化技巧以及如何借助工具来定位和分析程序的性能瓶颈,并提供了相关可以进行性能优化的工具或语言,希望能够更相关人员一些参考。


评论关闭