Cython第一章,概要
开一个新坑,写点关于《Cython》的读书笔记
这本书暂时还没有中译吧,我看起来还是有点吃力,
全书的示例代码在:https://github.com/cythonbook/examples,最好配合起来看。
cython的目的是要将python和c/c++结合起来,
python是一种高层级的,动态的,解释性的,易学的语言,但是其带来的副作用是,运行效率可能会比静态编译语言慢几个数量级。我们可以使用python调用外部接口的方式,极大的提高python的运行效率。
对比Python,C和Cython
cython、c语言扩展python、纯python、纯c代码他们之间的运行效率究竟有多少差异,我们通过实例来对比一下。
位置:/examples/01-essentials/03-timings
我们先来看setup.py
from distutils.core import setup, Extension
from Cython.Build import cythonize
exts = ([Extension("cfib", sources=["cfib.c", "cfib_wrap.c"])] +
cythonize("cyfib.pyx") +
cythonize([Extension("wrap_fib", sources=["cfib.c", "wrap_fib.pyx"])])
)
setup(
ext_modules = exts,
)
setup.py中编译了三个动态库,
- cfib.c + cfib_wrap.c->cfib.so (使用纯c代码编写的python扩展)
- cyfib.pyx ->cyfib.so(使用cython语法生成python扩展)
- cfib.c + wrap_fib.pyx-> wrap_fib.so(使用cython包装了cfib.c生成python扩展)(这个库没有用来进行性能比较)
再分析Makefile
.PHONY : all
all:
python setup.py build_ext -if
gcc -O3 cfib.c main.c -o cfib.x
.PHONY : clean
clean:
-rm -r build *.so *.pyc cyfib.c *.x wrap_fib.c
make的时候调用setup.py,同时也编译了cfib.c和main.c为一个可执行文件cfib.x。继续看cfib.c:
#include "cfib.h"
//计算斐波那契数列的第n个值
double cfib(int n) {
int i;
double a=0.0, b=1.0, tmp;
for (i=0; i<n; ++i) {
tmp = a; a = a + b; b = tmp;
}
return a;
}
看main.c:
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "cfib.h"
int main(int argc, char **argv) {
int arg=-1, numiter=-1, i;
clock_t t;
if (argc != 3) {
printf("Wrong number of arguments, expecting 2 (got %d)\n.", argc-1);
return 1;
}
arg = atoi(argv[1]);
numiter = atoi(argv[2]);
t = clock();
for (i=0; i<numiter; ++i) {
cfib(arg);
}
t = clock() - t;
printf("%f\n", ((float)t) / CLOCKS_PER_SEC / numiter * 1e9);
return 0;
}
可以看出main.c接受两个参数,调用cfib(arg)循环numiter次,然后的到总时间并计算出每次计算斐波那契数的平均时间,用纳秒来表示(10的-9次方秒),现在我们确定了cfib.x程序的作用。
需要对比以下四个库的效率,每个库都将计算fib(90)的值循环10万次,统计出每次fib计算的平均时间(纳秒)
- fib.py (纯python计算斐波那契数)
- cfib.c + cfib_wrap.c->cfib.so (使用纯c代码编写的python扩展)
- cyfib.pyx ->cyfib.so(使用cython语法生成python扩展)
- cfib.c + main.c ->cfib.x (纯c)
执行
make
python timings.py
在我的机器上计算的结果为:
纳秒
1.纯python:408
2.纯C扩展python模块cfib.so:139
3.Cython扩展python模块cyfib.so:62
4.纯C语言cfib.x:4
可以看出,对于计算斐波那契数这样的消耗cpu的运算来说,python比纯C语言程序慢两个数量级,手工使用python的C语言API进行扩展不如使用cython(因为cython对其进行了优化),cython对python的扩展可以提高1个数量级的效率
使用cython包装c代码(好像前面一个小结也设计到这个内容了)
查看对应位置的5个源文件,其中setup和Makefile都不是必须的,必要的时候可以自己编译,我们的目标是将wrap_fib.pyx 编译成wrap_fib.so,python可以直接import wrap_fib
位置:/examples/01-essentials/02-wrapping-c-code-with-cython/
源代码
cfib.h cfib.c wrap_fib.pyx setup.py Makefile
-----------------
cfib.h
#ifndef __CFIB_H__
#define __CFIB_H__
double cfib(int n);
#endif
-----------------
cfib.c
#include "cfib.h"
double cfib(int n) {
int i;
double a=0.0, b=1.0, tmp;
for (i=0; i<n; ++i) {
tmp = a; a = a + b; b = tmp;
}
return a;
}
------------------
wrap_fib.pyx
cdef extern from "cfib.h":
double cfib(int n)
def fib(n):
''' Returns the nth Fibonacci number.'''
return cfib(n)
-------------------
setup.py
from distutils.core import setup, Extension
from Cython.Build import cythonize
#Extention:wrap_fib(c扩展的名字),source:一个源码文件的列表
exts = cythonize([Extension("wrap_fib", sources=["cfib.c", "wrap_fib.pyx"])])
setup(
ext_modules = exts,
)
-------------------
Makefile
.PHONY : all
all:
python setup.py build_ext -if
.PHONY : clean
clean:
-rm -r build *.so wrap_fib.c
在cython中,我们将.pyx转化为.c,再编译成.so共享库,使用了python自带的distutils进行.c->.so的编译,有了以上这5个文件,再执行make,会发现文件夹里多了build/,wrap_fib.c,wrap_fib.so,其中wrap_fib.so为我们的目标库,可以直接被python引用。