Cython,加速python,保护代码(1):Overview


http://bbs.chinaunix.net/thread-4149295-1-1.html


cython简介

cython网上有很多介绍和教程之类的,我就不赘述了,简单“科普”一下。
cython是python的一个扩展模块,主要功用是将python代码编译成C/C++,然后再编译成python扩展模块(windows上是*.pyd)。
cython的最简单用法就是将”任何“合法的python代码直接编译为pyd,则其中的代码不经python解析器而直接调用python的api,这样你就可以隐藏你的源代码了,并且也获得了一点点的效率提升(官网数据10%-50%,我的测试结果为:生成50颗树的随机森林,平均时间从1.36秒左右降到1.00秒左右;生成500颗树的随机森林,平均时间从13
.9秒左右降到9.88秒左右;当然了,上面测试的串行训练树的结果,并行训练在其他博客里有说明,此处不记录)。

次简单用法就是将你的python代码中没有使用到动态特性的变量使用cython语法声明为静态类型,这样你就可以用对源代码进行最小的改动,获得很大的效率提升,而且代码可读性没有任何影响,还是“python代码”。生成500颗树的随机森林,平均时间从9.88秒左右降到8.88秒左右)。

最麻烦的当然是完全cython化的代码了。



cython语法(通过一些代码片段管中窥豹)
cython文件一般后缀为pyx(还有pxi/pxd)
cython语法代码前要增加cython标识,比如:cdef, cpdef等。

    1. 变量声明
   

  1. cdef int variable
  2. cdef float* variable
  3. cdef object variable  #python对象
复制代码


    2. 类实例变量声明
    使用cdef将所有实例变量在正常的类变量位置先声明,然后在__init__()中初始化
    有code有真相:
    ------------
   
  1. cdef class dtmfDetector:
  2. cdef int GOERTZEL_N, SAMPLING_RATE, debug
  3. cdef int* freqs
  4. def __init__(self, int pfreq=8000):
  5.      self.GOERTZEL_N = 92
  6.      self.SAMPLING_RATE = 8000
  7.      self.freqs = [697,770,852,941,1209,1336,1477,1633]
复制代码


    3. 让cython函数可以被其他模块的python代码调用
    将函数声明为cpdef即可,如果声明为cdef则为纯粹的c函数,其文档介绍即使将函数声明为cpdef,如果是其他c函数调用(包括cdef定义的函数),则cython会优化为cdef,效率也很高
    -----------
   
  1. cpdef reset(self):
  2.     self.sample_index = 0
复制代码


    4. 使用数组
   
  1. cdef float* variable = [0.1,1.2,2.3,3.4,5]
  2. variable[2] = 10.0
复制代码


    5. 循环优化
    可以放心的使用range循环,cython会将其优化为c类型的for语句形如: for (i = 0; i < 100; i++)
    或者你也可以使用cython特有的语法:
   
  1. for i from 0 < i < highvalue by 2: #by是步进,可选
复制代码


    6. 使用c/c++标准库函数
   
  1. from libc.string cimport memset
  2. import cython
  3. cdef int variable[5]
  4. memset(variable, 0, cython.sizeof(variable))
复制代码


    7. 直接调用c/c++源文件中的函数或类(python/cython/c混合编程)
   
  1. cdef extern from "otherfile.h":
  2.     int myCFunc() #myCFunc()在otherfile.h声明,可能在otherfile.c定义
  3.     cppclass MyCPlusClass: #在otherfile.h声明,可能在othercplusfile.cpp定义
  4.         MyCPlusClass()
  5.         void openDoor()

  6. def pythonFunc():
  7.         cdef MyCPlusClass* newclass = new MyCPlusClass()
  8.         cdef MyCPlusClass newclassInStack
  9.         print(myCFunc())
  10.         newclass.openDoor()
  11.         newclassInStack.openDoor()
  12.         del newclass #创建在堆上面的对象需要手工销毁
复制代码


编译pyx

    1. 安装cython和编译cython文件都需要c/c++编译环境,    如果你安装了VS,则一般不用更多设置,
    如果没有,在windows环境下建议安装Mingw,安装完成后将c:\MinGW添加到path系统变量,然后在C:\PythonXX\Lib\distutils\ 中添加一个
    distutils.cfg文件,内容为:
  
  1. [build]
  2. compiler = mingw32
  3. [build_ext]
  4. compiler = mingw32
复制代码


    2. 安装完cython后,如果C:\PythonXX\Scripts没有在path系统变量,也建议添加

    3. 直接执行 cython yourfile.pyx 则生成 yourfile.c 文件,然后使用编译器编译连接即可,稍复杂,网上很多教程,我就不再重复了,我这里说的是简单的方法

    4. 新建一个setup.py (名字可以随便起) :
   
  1. from distutils.core import setup
  2. from Cython.Build import cythonize
  3. setup(
  4.     name = 'Your module',
  5.     ext_modules = cythonize("yourfile.pyx")
  6. )
复制代码


    5. 执行命令:
   
  1. python.exe setup.py build_ext --inplace
复制代码

    则在yourfile.pyx 同一目录下生成yourfile.pyd

    6. 使用yourfile.pyd和yourfile.py一样,没有任何区别,
   
  1. import yourfile ...
复制代码


    7. cython -a youfile.pyx 则生成yourfile.c的同时还会生成yourfile.html文件,列出cython代码行和c代码行的一一对应关系,点击代码行则显示对应的c代码,特别方便进行代码优化。




profiling
    使用cython的目的是效率,你肯定关心cython究竟给我提高了多少效率,所以“测速”的功能肯定是不能缺的。

    1. 在pyx文件的头两行内添加一行:
   
  1. # cython: profile=True
复制代码


    2. 测试代码:
   
  1. import yourmodule, pstats, cProfile
  2. cProfile.runctx("yourmodule.func(args)", globals(), locals(), "Profile.prof")
  3. s = pstats.Stats("Profile.prof")
  4. s.strip_dirs().sort_stats("time").print_stats()
复制代码

    则打印出每个函数的调用次数和执行时间等内容
    注意在发行你的软件时切换profile=False或删除此行





一个更完整的例子:

http://docs.cython.org/src/tutorial/cython_tutorial.html

Primes

Here’s a small example showing some of what can be done. It’s a routine for finding prime numbers. You tell it how many primes you want, and it returns them as a Python list.

primes.pyx:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
def primes(int kmax):
    cdef int n, k, i
    cdef int p[1000]
    result = []
    if kmax > 1000:
        kmax = 1000
    k = 0
    n = 2
    while k < kmax:
        i = 0
        while i < k and n % p[i] != 0:
            i = i + 1
        if i == k:
            p[k] = n
            k = k + 1
            result.append(n)
        n = n + 1
    return result

You’ll see that it starts out just like a normal Python function definition, except thatthe parameter kmax is declared to be of type int . This means that the object passed will be converted to a C integer(or a TypeError. will be raised if it can’t be).

Lines 2 and 3 use the cdef statement to define some local C variables.Line 4 creates a Python list which will be used to return the result. You’ll notice that this is done exactly the same way it would be in Python. Because the variable result hasn’t been given a type, it is assumed to hold a Python object.

Lines 7-9 set up for a loop which will test candidate numbers for primeness until the required number of primes has been found. Lines 11-12, which try dividing a candidate by all the primes found so far, are of particular interest.Because no Python objects are referred to, the loop is translated entirely into C code, and thus runs very fast.

When a prime is found, lines 14-15 add it to the p array for fast access by the testing loop, andline 16 adds it to the result list. Again, you’ll notice that line 16 looks very much like a Python statement, and in fact it is, with the twist that the C parameter n is automatically converted to a Python object before being passed to the append method. Finally, at line 18, a normal Python return statement returns the result list.

Compiling primes.pyx with the Cython compiler produces an extension module which we can try out in the interactive interpreter as follows:

>>> import primes
>>> primes.primes(10)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

See, it works! And if you’re curious about how much work Cython has saved you, take a look at the C code generated for this module.




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值