python C盘扩展

python C扩展

也许通过profile,我们已经找到了性能热点,但这个热点就是要运行大量的计算,而且没法cache,没法省略。。。这个时候就该python的C扩展出马了,C扩展就是把部分python代码用C或者C++重新实现,然后编译成动态链接库,提供接口给其它python代码调用。由于C语言的效率远远高于python代码,所以使用C扩展是非常普遍的做法,比如我们前面提到的cProfile就是基于_lsprof.so的一层封装。python的大所属对性能有要求的库都使用或者提供了C扩展,如gevent、protobuff、bson。
  笔者曾经测试过纯python版本的bson和cbson的效率,在综合的情况下,cbson快了差不多10倍!
  python的C扩展也是一个非常复杂的问题,本文仅给出一些注意事项:
第一:注意引用计数的正确管理
  这是最难最复杂的一点。我们都知道python基于指针技术来管理对象的生命周期,如果在扩展中引用计数出了问题,那么要么是程序崩溃,要么是内存泄漏。更要命的是,引用计数导致的问题很难debug。。。
  C扩展中关于引用计数最关键的三个词是:steal reference,borrowed reference,new reference。建议编写扩展代码之前细读python的官方文档
第二:C扩展与多线程
  这里的多线程是指在扩展中new出来的C语言线程,而不是python的多线程,出了python doc里面的介绍,也可以看看《python cookbook》的相关章节。
第三:C扩展应用场景
  仅适合与业务代码的关系不那么紧密的逻辑,如果一段代码大量业务相关的对象 属性的话,是很难C扩展的
  将C扩展封装成python代码可调用的接口的过程称之为binding,Cpython本身就提供了一套原生的API,虽然使用最为广泛,但该规范比较复杂。很多第三方库做了不同程度的封装,以便开发者使用,比如boost.python、cython、ctypes、cffi(同时支持pypy cpython),具体怎么使用可以google。

beyond CPython

尽管python的性能差强人意,但是其易学易用的特性还是赢得越来越多的使用者,业界大牛也从来没有放弃对python的优化。这里的优化是对python语言设计上、或者实现上的一些反思或者增强。这些优化项目一些已经夭折,一些还在进一步改善中,在这个章节介绍目前还不错的一些项目。

cython

前面提到cython可以用到binding c扩展,但是其作用远远不止这一点。
  Cython的主要目的是加速python的运行效率,但是又不像上一章节提到的C扩展那么复杂。在Cython中,写C扩展和写python代码的复杂度差不多(多亏了Pyrex)。Cython是python语言的超集,增加了对C语言函数调用和类型声明的支持。从这个角度来看,cython将动态的python代码转换成静态编译的C代码,这也是cython高效的原因。使用cython同C扩展一样,需要编译成动态链接库,在linux环境下既可以用命令行,也可以用distutils。
  如果想要系统学习cython,建议从cython document入手,文档写得很好。下面通过一个简单的示例来展示cython的使用方法和性能(linux环境)。
  首先,安装cython:

pip install Cython

下面是测试用的python代码,可以看到这两个case都是运算复杂度比较高的例子:
[复制代码](javascript:void(0)?

# -*- coding: UTF-8 -*-
def f(x):
return x**2-x
def integrate_f(a, b, N):
s = 0
dx = (b-a)/N
for i in range(N):
s += f(a+i*dx)
return s * dx
def main():
import time
begin = time.time()
for i in xrange(10000):
for i in xrange(100):f(10)
print 'call f cost:', time.time() - begin
begin = time.time()
for i in xrange(10000):
integrate_f(1.0, 100.0, 1000)
print 'call integrate_f cost:', time.time() - begin
if __name__ == '__main__':
main()

[复制代码](javascript:void(0)?

运行结果:

call f cost: 0.215116024017

call integrate_f cost: 4.33698010445

不改动任何python代码也可以享受到cython带来的性能提升,具体做法如下:

  • step1:将文件名(cython_example.py)改为cython_example.pyx

  • step2:增加一个setup.py文件,添加一下代码:

  • [复制代码](javascript:void(0)?

    1 from distutils.core import setup
    2 from Cython.Build import cythonize
    3 
    4 setup(
    5   name = 'cython_example',
    6   ext_modules = cythonize("cython_example.pyx"),
    7 )
       778570108 群里有志同道合的小伙伴,互帮互助。群里有视频学习教程和PDF,一起学习,共同进步!
    

    [复制代码](javascript:void(0)?

  • step3:执行python setup.py build_ext --inplace
    img
      可以看到 增加了两个文件,对应中间结果和最后的动态链接库

  • step4:执行命令 python -c “import cython_example;cython_example.main()”(注意: 保证当前环境下已经没有 cython_example.py)
    运行结果:
    call f cost: 0.0874309539795
    call integrate_f cost: 2.92381191254
    性能提升了大概两倍,我们再来试试cython提供的静态类型(static typing),修改cython_example.pyx的核心代码,替换f()和integrate_f()的实现如下:
    [复制代码](javascript:void(0)?

 1 def f(double x): # 参数静态类型
 2     return x**2-x
 3 
 4 def integrate_f(double a, double b, int N):
 5     cdef int i
 6     cdef double s, dx
 7     s = 0
 8     dx = (b-a)/N
 9     for i in range(N):
10         s += f(a+i*dx)
11     return s * dx

[复制代码](javascript:void(0)?
然后重新运行上面的第三 四步:结果如下
call f cost: 0.042387008667
call integrate_f cost: 0.958620071411
上面的代码,只是对参数引入了静态类型判断,下面对返回值也引入静态类型判断。
替换f()和integrate_f()的实现如下:
[复制代码](javascript:void(0)?

 1 cdef double f(double x): # 返回值也有类型判断
 2     return x**2-x
 3 
 4 cdef double integrate_f(double a, double b, int N):
 5     cdef int i
 6     cdef double s, dx
 7     s = 0
 8     dx = (b-a)/N
 9     for i in range(N):
10         s += f(a+i*dx)
11     return s * dx

[复制代码](javascript:void(0)?
然后重新运行上面的第三 四步:结果如下
call f cost: 1.19209289551e-06
call integrate_f cost: 0.187038183212
mazing!

pypy

pypy是CPython的一个替代实现,其最主要的优势就是pypy的速度,下面是官网的测试结果:
  在实际项目中测试,pypy大概比cpython要快3到5倍!pypy的性能提升来自JIT Compiler。在前文提到google的Unladen Swallow 项目也是想在CPython中引入JIT,在这个项目失败后,很多开发人员都开始加入pypy的开发和优化。另外pypy占用的内存更少,而且支持stackless,基本等同于协程。
  pypy的缺点在于对C扩展方面支持的不太好,需要使用CFFi来做binding。对于使用广泛的library来说,一般都会支持pypy,但是小众的、或者自行开发的C扩展就需要重新封装了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值