用Cython加速Python程序以及包裝C程序簡單測試


用Cython加速Python程序

我沒有拼錯,就是Cython,C+Python=Cython!
我們來看看Cython的威力,先運行下邊的程序:

import time

def fib(n):
    if n==0:
        return 0
    if n==1:
        return 1
    return fib(n-1)+fib(n-2)
t=time.time()
print(fib(40))
print(time.time()-t)

$ python fib.py
102334155
59.367255449295044

在我的渣渣筆記本上,用時59.3秒,差不多一分鍾。當然,在你那可能比我快一點,這也很正常。
好了,我們再試試Cython:

$ cython fib.py --embed     
$ gcc -O3 fib.c -I /usr/include/python3.5m/ -lpython3.5m
$ ./a.out  
102334155  
14.487313747406006

嗯,快了那么一點點,4倍左右;我解釋一下前邊的幾句代碼:
首先,用cython命令把python生成c文件,也就是cython fib.py會生成一個fib.c的文件
--embed參數就是自動生成一個main函數,以便讓gcc生成可執行程序。
接下來就是用gcc把fib.c編譯成了個a.out程序,運行之,結果快了4倍(從60秒減少到15秒以內)。
當然,這只是小試牛刀,區區4倍而已,這也太少了!
接下來我吧這個文件復制成fib.pyx,並修改了一句代碼:

import time

cdef int fib(int n):
    if n==0:
        return 0
    if n==1:
        return 1
    return fib(n-1)+fib(n-2)
t=time.time()
print(fib(40))
print(time.time()-t)

我只改了1句,就是把 def fib(n):改成了 cdef int fib(int n):,也就是加了一個類型,下邊讓我們見證奇跡:

$ cython fib.pyx --embed
$ gcc -O3 fib.c -I /usr/include/python3.5m/ -lpython3.5m 
$ ./a.out 
102334155
0.45729994773864746

沒有看錯,現在只需要0.45秒!性能提升了132倍。
這個0.45秒算是什么樣的速度呢?下邊,我照貓畫虎,寫了基本相同的一段C程序:
#include "stdio.h"
#include "time.h"

static int fib(int n){
    if(n==0)
            return 0;
    if(n==1)
            return 1;
    return fib(n-1)+fib(n-2);
}
int main(){
    clock_t t=clock();
    printf("%d\n",fib(40));
    printf("%f sec\n",(clock()-t)/1000.0/1000.0);
}

這個跟python寫的基本一模一樣,只是換成了C語法,然后:

$ gcc -O3 fib.c  
$ ./a.out  
102334155  
0.452981 sec  

天,只比剛才Cython的程序慢了0.005秒(我覺得這已經是誤差了)
是不是感覺Cython碉堡了?(基本用Python的語法,實現了C的速度。
其實,這才剛剛開始。畢竟雖然Python代碼寫起來比C溜好多,但以前的C代碼怎么辦?並且,一些C實現的算法
用Cython改寫也不是特別方便,能不能直接拿來就用呢?當然能,並且也可以很6。
比如上邊的那個fib函數,我已經用C寫完了,怎么整合到Python里邊呢?
首先,我先把C里邊的main函數去掉,改成下邊的樣子:

#include "stdio.h"
#include "time.h"

static int fib(int n){
    if(n==0)
            return 0;
    if(n==1)
            return 1;
    return fib(n-1)+fib(n-2);
}

其實這時,我們已經可以用gcc編譯成一個鏈接庫,用ctypes調用了,然而在Cython看來,
這太(調)不(用)清(麻)真(煩),我們只需要2句代碼:

cdef extern from "fib.c": 
    int fib(int)

def fibf(n):
    return fib(n)

雖然是4行,其實也就是2句無疑:)
第一句我先把fib函數從C文件里邊導入,然后又定義了一個fibf的函數,把導進來的函數又調用了一下。
cdef的作用,就是把外部函數導出為cython能調用的函數,def的作用就是定義python能調用的函數了。
把這個文件保存成fibf.pyx,然后:

cython fibf.pyx
gcc fibf.c -shared -fPIC -I /usr/include/python3.5m -lpython3.5m -o fibf.so -O3

把這個文件編譯成了一個fibf.so文件
然后寫了下邊的python代碼測試:

$cat test.py 
import time
import fibf 
t=time.time()
print(fibf.fibf(40))
print(time.time()-t)


python test.py 
102334155
0.47469592094421387

也就是說,2句代碼,就把一個C語言寫的代碼。包裝成了一個python能直接import的庫。是不是方便極了……
反正個人覺得比ctypes方便。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM