【Faster RCNN系列】cython distutils 以及setup

  

setup这一年也是遇到了很多次,随着python编程学习的不断深度对于python的了解也不断在增加,这里做一次简单的小节。

相关工具:distutils,cython

1.Cython简介

我们平时使用的python,又叫CPython,因为他是用C语言写的,一般来说,我们的python源代码(.py沃森件),首先编译成字节码(.pyc文件),然后将.pyc文件放在python虚拟机上运行,这里的python虚拟机就是所谓的“python解释器”。总而言之,纯python代码的运行速度介于传统的编译语言和传统的解释语言之间。

2.1什么是字节码?

字节码在python虚拟机程序里对应的是PyCodeObject对象; .pyc文件是字节码在磁盘上的表现形式。

每一个以py结尾的python源代码文件都是模块,其中那个启动后能够运行整个程序的文件叫顶层文件。而顶层文件导入其他模块(文件),必须找到文件然后将其编译成字节码,并且运行字节码。

导入文件时编译的字节码会自动保存,同时保存的还有时间戳。如果同时存在.py和.pyc,python会使用.pyc运行,如果.pyc的编译时间早于.py的时间,则重新编译.py,并更新.pyc文件。

如果python无法在机器上写入字节码,程序仍然可以工作,字节码会在内存中生成并在程序结束时丢弃掉。(严格而讲,只有文件导入的情况下字节码才会保存,并不是对顶层文件)。

如果想生成test.pyc,我们可以使用python内置模块py_compile来编译。也可以执行命令 python -m test.py 这样,就生成了test.pyc。

typedef struct {
    PyObject_HEAD
    int co_argcount;        /* 位置参数个数 */
    int co_nlocals;         /* 局部变量个数 */
    int co_stacksize;       /* 栈大小 */
    int co_flags;   
    PyObject *co_code;      /* 字节码指令序列 */
    PyObject *co_consts;    /* 所有常量集合 */
    PyObject *co_names;     /* 所有符号名称集合 */
    PyObject *co_varnames;  /* 局部变量名称集合 */
    PyObject *co_freevars;  /* 闭包用的变量名集合 */
    PyObject *co_cellvars;  /* 内部嵌套函数引用的变量名集合 */
    /* The rest doesn’t count for hash/cmp */
    PyObject *co_filename;  /* 代码所在文件名 */
    PyObject *co_name;      /* 模块名|函数名|类名 */
    int co_firstlineno;     /* 代码块在文件中的起始行号 */
    PyObject *co_lnotab;    /* 字节码指令和行号的对应关系 */
    void *co_zombieframe;   /* for optimization only (see frameobject.c) */
} PyCodeObject;

2.2执行字节码?

Python虚拟机的原理就是模拟可执行程序再X86机器上的运行,X86的运行时栈帧如下图:

当发生函数调用时,创建新的栈帧,对应Python的实现就是PyFrameObject对象。PyFrameObject对象创建程序运行时的动态信息,即执行环境,相关源码大致如下:

typedef struct _frame{  
    PyObject_VAR_HEAD //"运行时栈"的大小是不确定的  
    struct _frame *f_back; //执行环境链上的前一个frame,很多个PyFrameObject连接起来形成执行环境链表  
    PyCodeObject *f_code; //PyCodeObject 对象,这个frame就是这个PyCodeObject对象的上下文环境  
    PyObject *f_builtins; //builtin名字空间  
    PyObject *f_globals;  //global名字空间  
    PyObject *f_locals;   //local名字空间  
    PyObject **f_valuestack; //"运行时栈"的栈底位置  
    PyObject **f_stacktop;   //"运行时栈"的栈顶位置  
    //...  
    int f_lasti;  //上一条字节码指令在f_code中的偏移位置  
    int f_lineno; //当前字节码对应的源代码行  
    //...  
      
    //动态内存,维护(局部变量+cell对象集合+free对象集合+运行时栈)所需要的空间  
    PyObject *f_localsplus[1];    
} PyFrameObject;

每一个 PyFrameObject对象都维护了一个 PyCodeObject对象,这表明每一个 PyFrameObject中的动态内存空间对象都和源代码中的一段Code相对应。

2.2什么是Cython?

Cython是Python语言的扩展模块,他的目的在于称为python语言的超集(superset),为python提供高级的,面向对象的,函数式的和动态的编程。他的主要功能是支持(可选)部分静态类型的声明作为Cython语言的一部分。这样cython的源代码就可以被转化为优化过的C/C++代码,然后可以将这些代码编程称为python的扩展模块。Cython代码在CPython运行时环境中执行,但是以编译的C的速度执行,并且能够直接调用C库。同时,它保留了Python源代码的原始接口,这使得它可以直接从Python代码中使用。

2.3构建Cython

Cython代码必须编译,具体包括两步:

  第一步,将A.pyx文件用Cython编译到一个.c文件中,其中含有python扩展模块的代码

  第二步,将.c文件编译成.so文件(Windows下为.pyd文件),文件可以 直接import进入Python session。Distutils或setuptools负责这部分。虽然Cython可以在某些情况下为你调用它们。

具体实例,在faser rcnn中,bbox_overlaps函数就行用cython写的。

 

  具体构建Cython的几种方法:

    1.写一个distutils / setuptools setup.py。

setyp.py:

from distutils.core import setup
from Cython.Build import cythonize

setup(
    ext_modules = cythonize("helloworld.pyx")
)

命令行:

$ python setup.py build_ext --inplace

    2.使用Pyximport,导入Cython .pyx文件就像它们是.py文件一样(使用distutils在后台编译和构建)。这种方法比编写一个setup.py容易一些。但不是很灵活,比如,您需要某些编译选项。(其实,没有学习编译原理的我不需要哪些编译选项,显然这种方法是极好的)。但是,我的电脑在使用这种方法的时候,失败了,可能是VS环境的原因吧,所以我采用了第一种方法,直接编译了一个pyd文件使用。

import pyximport; pyximport.install()
import helloworld
# Hello World

    3.Jupyter notebook允许内联Cython代码。这是开始编写Cython代码并运行它的最简单方法。

2.distutils简介

除了Cython中使用到setup.py,编写python的第三方库,也是要编写setup.py的。其实如果我们下载过一些第三库的源代码文件,打开之后一般就会有一个setup.py,执行python setup.py install 就可以安装这个库了。setup.py 如何编写内容很多,可以参考官方文档:https://wiki.python.org/moin/Distutils/Tutorial?highlight=%28setup.py%29。一个典型的setup.py的写法如下(参考自官方文档):

文件结构为:
top
|-- package
|   |-- __init__.py  # 空的
|   |-- module.py  # 这个package中的模块文件
|   `-- things
|       |-- cross.png
|       |-- fplogo.png
|       `-- tick.png
|-- README
`-- setup.py

setup.py:

from distutils.core import setup

#This is a list of files to install, and where
#(relative to the 'root' dir, where setup.py is)
#You could be more specific.
files = ["things/*"]

setup(name = "appname",
    version = "100",
    description = "yadda yadda",
    author = "myself and I",
    author_email = "email@someplace.com",
    url = "whatever",
    #Name the folder where your packages live:
    #(If you have other packages (dirs) or modules (py files) then
    #put them into the package directory - they will be found 
    #recursively.)
    packages = ['package'],
    #'package' package must contain files (see list above)
    #I called the package 'package' thus cleverly confusing the whole issue...
    #This dict maps the package name =to=> directories
    #It says, package *needs* these files.
    package_data = {'package' : files },
    #'runner' is in the root.
    scripts = ["runner"],
    long_description = """Really long text here.""" 
    #
    #This next part it for the Cheese Shop, look a little down the page.
    #classifiers = []     
) 

 Making the installation tarball

python setup.py sdist

我的编译过程:

setup.py:

import sys
import numpy as np
A=sys.path.insert(0, "..")
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
from Cython.Distutils import build_ext

# ext_module = cythonize("TestOMP.pyx")
ext_module = Extension(
                        "bbox",
            ["bbox.pyx"],
            extra_compile_args=["/openmp"],
            extra_link_args=["/openmp"],
            )

setup(
    cmdclass = {'build_ext': build_ext},
    ext_modules = [ext_module],
    #注意这一句一定要有,不然只编译成C代码,无法编译成pyd文件
    include_dirs=[np.get_include()]
)

Cython官网教程:http://docs.cython.org/en/latest/index.html(中文版:https://moonlet.gitbooks.io/cython-document-zh_cn/content/ch1-basic_tutorial.html)

 参考博客:   https://www.cnblogs.com/webber1992/p/6597166.html

      https://blog.csdn.net/runner668/article/details/80137962

      https://www.cnblogs.com/lyrichu/p/6818008.html

      https://www.cnblogs.com/lyrichu/p/6818008.html

      https://www.cnblogs.com/UnGeek/p/5922630.html

原文地址:https://www.cnblogs.com/SsoZhNO-1/p/9987515.html