python

<cython>学习笔记第一章:概要

2015-06-09  本文已影响597人  Stansosleepy

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中编译了三个动态库,

  1. cfib.c + cfib_wrap.c->cfib.so (使用纯c代码编写的python扩展)
  2. cyfib.pyx ->cyfib.so(使用cython语法生成python扩展)
  3. 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计算的平均时间(纳秒)

  1. fib.py (纯python计算斐波那契数)
  2. cfib.c + cfib_wrap.c->cfib.so (使用纯c代码编写的python扩展)
  3. cyfib.pyx ->cyfib.so(使用cython语法生成python扩展)
  4. 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引用。

上一篇下一篇

猜你喜欢

热点阅读