cython使用方法(不是 CPython)

cython概览

参考文献:Cython的用法以及填坑姿势

为什么用cython(注意区别于 CPython)

因为项目需要,需要优化已有的Python代码。目前Python代码的执行过程是将Python代码转变成一行行指令,然后解释器解释指令的执行,调用到C代码层。如果去掉指令解释这个阶段,直接进入C代码层,效率就比较高了。如果用之前所述的使用Python C API将Python代码改造为C代码并作为Python的内建模块,工作量极其大,也不能保证其正确性,所以这种方法不太现实。而Cython库正好符合这种场景需求,将已有的Python代码转化为C语言的代码,并作为Python的built-in模块扩展。

Cython语言使得Python语言的C扩展与Python本身一样简单。Cython语言是Python语言的一个超集(几乎所有的Python代码是有效的),但Cython还支持可选的静态类型来调用C函数,使用C++类和声明快C类型变量和类的属性。这允许编译器从Cython代码生成非常高效的C代码。
这使得Cython编写外部C / C++库代码的理想语言,和快速的C模块,提高Python代码的执行速度。

Cython 可以让我们方便地:

  • 用 Python 的语法混合编写 Python 和 C/C++ 代码,提升 Python 速度
  • 调用 C/C++ 代码

cython相关文件类型

需要了解的几种Python文件类型介绍:

  • .py : python的源代码文件
  • .pyx : python的c扩展文件(cython使用该类型的文件,代码要符合cython的规范)
  • .pyc: Python源代码import后,编译生成的字节码
  • .pyo: Python源代码编译优化生成的字节码。pyo比pyc并没有优化多少,只是去掉了断言
  • .pyd: Python的动态链接库(Windows平台)
  • .so : Python的动态链接库(Linux平台)

.py, .pyc, .pyo 运行速度几乎无差别,只是pyc, pyo文件加载的速度更快,不能用文本编辑器查看内容,反编译不太容易

cython工作流程

pyx文件是python的c扩展文件,代码要符合cython的规范,用什么编辑器写都行。

上面的pyx文件还仅仅是源代码文件,要想被python调用、要想运行,仅仅写了源代码还是不够的。具体来说,还要转成.c或者.c++的文件,并且再进一步转成.pyd或.so文件,pyd或so文件才是可以直接使用的文件。在这里插入图片描述

cython基本使用方法

(1)在使用Cython编译Python代码时,务必要安装C/C++编译器(windows可以直接安装Visiual Studio 的开发环境)
(2)安装Cython库:
pip install Cython
(3)编写.pyx文件,如test.pyx(或者直接写.py也可以?只要文件里不使用cpython独有的语法,而是使用纯python语法)

def say_hello():
    print "hello world"

def say_hello():
    print "hello world"

(4)编写.setup文件

  • from distutils.core import setup
    from Cython.Build import cythonize
    setup(ext_modules = cythonize("test.py"))
    其中,
    cythonize()是Cython提供将Python代码转换成C代码的API,
    setup是Python提供的一种发布Python模块的方法。

     

(5)编译
使用命令行编译Python代码:

  • python setup.py build_ext  --inplace
    其中,
    build_ext是指明python生成C/C++的扩展模块(build C/C++ extensions (compile/link to build directory))
    --inplace指示 将编译后的扩展模块直接放在与test.py同级的目录中。
    

     

生成的目录结构如下:

  • test.c是test.py转化后的C代码文件,可以看到test.c非常大!(linux下生成.cpp文件)
  • test.pyd是python的动态链接库,我们在使用import test时会加载
  • build目录编译过程中生成的临时文件
    在这里插入图片描述
    (6)使用
    使用刚刚生成的test模块,就像使用Python的任意模块一样,直接import即可:
    在这里插入图片描述

cython 定义静态,提升效率

可以在cython代码的某些地方加上静态类型声明,也可以更进一步提升Python的运行效率
使用关键字:cde

  • def say_hello(int s):
        cdef int a = 2
        print s + 2

     

s和a变量直接指示为int类型,不用再做动态语言的类型推断了。
如:

import math
import time
def f():
    time1 = time.time()
    for i in range(100000000):
        x = math.sqrt(i)
    time2 = time.time()
    print time2 - time1

 

这段原生的Python代码运行时间是13.17秒,使用Cython优化后,运行时间为9.36秒。基本上提升30%。其实Cython一般对外声称的效率提升也大概是这么多。

用Cython包装C++代码,提供给python调用

【核心概要】
(1) 定义.h和.cpp文件(C++头文件和源文件);
(2) 定义.pyx文件(Cython文件);

  • 声明Cython中的C++类接口,包装C++类:cdef extern from “Rectangle.h” namespace “shapes”:
  • 声明Cython类,用于使用C++类:cdef cppclass Rectangle:
  • 处理结构体:Rectangle(int, int, int, int) except +,except +让Cython能够识别并容纳C++的错误;
  • 声明属性和变量:int x0, y0, x1, y1; int getArea(); void getSize(int* width, int* height); void move(int, int);
  • 用C++包装类声明变量:rec_ptr = new Rectangle(1, 2, 3, 4);
  • 创建Cython包装函数:cdef class PyRectangle:# 定义python函数,返回cython调用的C++函数计算结果;

(3) 编写setup.py
(4) 编译.pyx和.cpp.h文件,链接到一起(通过Setup.py文件);
(5) 定义并运行.py文件(python文件),得到计算结果;

[用Cython包装C++代码,C++调用CUDA代码]

以检测算法中的nms算法为例。
因为nms算法调用次数非常多,很消耗计算,所以通过cuda进行加速。

工程目录如下所示:
其中cpu_nms.pyx不属于跟CUDA相关的部分,不予考虑。
在这里插入图片描述
具体步骤如下:
(1) 编写cuda加速文件。
在.cu文件内定义C函数,并实现该函数(本示例中为nms_kernel.cu):

void _nms(int* keep_out, int* num_out, const float* boxes_host, int boxes_num,
          int boxes_dim, float nms_overlap_thresh, int device_id)
          {
          ........
          }

(2)定义一个头文件.hpp(本示例中为gpu_nms.hpp),声明该函数:

  • void _nms(int* keep_out, int* num_out, const float* boxes_host, int boxes_num,
              int boxes_dim, float nms_overlap_thresh, int device_id);

     

(3)用cython语法编写.pyx文件(本示例中为gpu_nms.pyx):
该文件中,首先声明调用了外部的函数:

  • cdef extern from "gpu_nms.hpp":
        void _nms(np.int32_t*, int*, np.float32_t*, int, int, float, int)

     

然后其他函数(gpu_nms)就可以调用这个cuda中的函数了。
在这里插入图片描述
(4)编写setup.py,将.cu和.pyx作为其编译的对象。
在这里插入图片描述
备注:setup.py文件中还有许多其他的操作,暂未看懂,有待学习。
(5)编译,其他文件就可以用python的方式调用.pyx文件(本示例为gpu_nms.pyx)中的函数(gpu_nms)了。
本示例中,有一个nms.py文件,调用了gpu_nms.pyx中的gpu_nms( )函数,如下所示。

<span style="color:#000000"><code class="language-python"><span style="color:#0077aa">from</span> <span style="color:#999999">.</span>gpu_nms <span style="color:#0077aa">import</span> gpu_nms
</code></span>
  • 1

编译后,会根据.pyx生成.so(windows下为.pyd)+.cpp文件,新的目录结构如下(cpu_nms的三个文件不是cuda相关的,无需看):
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值