成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Python中擴(kuò)展C語(yǔ)言加快執(zhí)行速度的實(shí)現(xiàn)方法

MudOnTire / 771人閱讀

摘要:通過(guò)執(zhí)行時(shí)間對(duì)比可以發(fā)現(xiàn)調(diào)用函數(shù)來(lái)擴(kuò)展功能可以大大提高執(zhí)行速度,而自帶的庫(kù)由于在源生代碼上進(jìn)行封裝,執(zhí)行時(shí)間會(huì)高于源生代碼擴(kuò)展方式,但庫(kù)使用方便,特別適合應(yīng)用在第三方封裝代碼,提供動(dòng)態(tài)鏈接庫(kù)和調(diào)用文檔的場(chǎng)合。

前言

當(dāng)我們提到一門(mén)編程語(yǔ)言的效率時(shí),通常包含了開(kāi)發(fā)效率和運(yùn)行效率這兩層意思。Python作為一門(mén)高級(jí)語(yǔ)言,它功能強(qiáng)大,易于掌握,能夠快速的開(kāi)發(fā)軟件,“l(fā)ife?is?short,we?use?python!”,想必這些優(yōu)點(diǎn)是毋庸置疑的,但是作為一門(mén)解釋性語(yǔ)言,執(zhí)行速度的局限性導(dǎo)致在處理某些高頻任務(wù)時(shí)存在不足。
由于Python本身由C語(yǔ)言實(shí)現(xiàn)的,開(kāi)發(fā)性能要求較高的程序模塊可以通過(guò)擴(kuò)展運(yùn)行效率更高的C語(yǔ)言來(lái)彌補(bǔ)自身的弱點(diǎn)。另外有些算法已經(jīng)有開(kāi)源的C庫(kù),那么也沒(méi)必要用Python重寫(xiě)一份,只需要通過(guò)Python進(jìn)行C庫(kù)的調(diào)用即可。
本文通過(guò)實(shí)例介紹如何在Python 程序中整合既有的C語(yǔ)言模塊,從而充分發(fā)揮Python 語(yǔ)言和 C 語(yǔ)言各自的優(yōu)勢(shì)。


Python實(shí)現(xiàn)測(cè)試函數(shù)

使用Python編寫(xiě)一個(gè)遞歸函數(shù)和循環(huán)函數(shù),應(yīng)用Python的計(jì)時(shí)庫(kù)timeit測(cè)試函數(shù)執(zhí)行10000次所需要的時(shí)間分別為57ms和41ms。

實(shí)現(xiàn)代碼如下:
from timeit import timeit? 

def factorial(n):
?? ?if n<2:return 1
return factorial(n-1)*n 
def rooporial(n):
?? ?if n<2:return 1
?? ?ans = 1
?? ?for i in range(1,n+1):
?? ??? ?ans *=i
?? ?return ans 

if __name__ == "__main__": 
print "factorial",factorial(20),timeit("factorial(20)","from __main__ import factorial",number=10000) #timeit(‘函數(shù)名’,‘運(yùn)行環(huán)境’,number=運(yùn)行次數(shù))
print "rooporial",rooporial(20),timeit("rooporial(20)","from __main__ import rooporial",number=10000)
打印返回:
factorial 2432902008176640000 0.0578598976135
factorial 2432902008176640000 0.0410023010987

當(dāng)然遞歸方法使程序的結(jié)構(gòu)簡(jiǎn)潔,但由于它逐層深入調(diào)用的機(jī)制使得執(zhí)行效率不如循環(huán),以下的測(cè)試可以發(fā)現(xiàn)每一次遞歸是新一次的函數(shù)調(diào)用,會(huì)產(chǎn)生新的局部變量,增加了執(zhí)行時(shí)間。但是即使使用For循環(huán)實(shí)現(xiàn)也需要41ms時(shí)間,接下來(lái)我們嘗試更快的實(shí)現(xiàn)方式。

測(cè)試代碼如下:
def up_add_down(n):
?? ?print("level %d: n location %p
",n,id(n))
?? ?if n<=4:up_add_down(n+1)
?? ?print("level %d: n location %p
",n,id(n))
?? ?return 
打印返回:
("level %d: n location %p
", 0, 144136380) ("level %d: n location %p
", 1, 144136368) 
("level %d: n location %p
", 2, 144136356) ("level %d: n location %p
", 3, 144136344) 
("level %d: n location %p
", 4, 144136332) ("level %d: n location %p
", 5, 144136320) 
("level %d: n location %p
", 5, 144136320) ("level %d: n location %p
", 4, 144136332) 
("level %d: n location %p
", 3, 144136344) ("level %d: n location %p
", 2, 144136356) 
("level %d: n location %p
", 1, 144136368) ("level %d: n location %p
", 0, 144136380)

Python源生調(diào)用實(shí)現(xiàn)

Python在設(shè)計(jì)之初就考慮到通過(guò)足夠抽象的機(jī)制讓C和C++之類的編譯型的語(yǔ)言導(dǎo)入到Python腳本代碼中,在Python的官方網(wǎng)站上也找到了擴(kuò)展和嵌入Python解釋器對(duì)應(yīng)的方法。鏈接為:https://docs.python.org/2.7/e...。這里介紹下如何將C編寫(xiě)的函數(shù)擴(kuò)展至Python解釋器中。

(1)將C編寫(xiě)的遞歸函數(shù)存為wrapper.c,作為Python的擴(kuò)展庫(kù)。同時(shí)需要對(duì)C函數(shù)增加一個(gè)型如PyObject* Module_func()的封裝接口,該接口用于Python解釋器的交互。將封裝接口加入至型如PyMethodDef ModuleMethods[]的數(shù)組中,Python解釋器能夠從數(shù)組中導(dǎo)入并調(diào)用到封裝接口。最后是實(shí)現(xiàn)對(duì)擴(kuò)展庫(kù)的初始化函數(shù),調(diào)用Py_InitModule()函數(shù),把擴(kuò)展庫(kù)和ModuleMethods[]數(shù)組的名字傳遞進(jìn)去,以便于解釋器能正確的調(diào)用庫(kù)中的函數(shù)。

wrapper.c實(shí)現(xiàn)代碼如下:
#include 

unsigned long long factorial(int n)
{
?? ?if(n<2)return 1;
?? ?return factorial(n-1)*n;
}

PyObject* wrap_fact(PyObject* self,PyObject* args)
{
?? ?int n;
?? ?unsigned long long? result;
?? ?
?? ?if(!PyArg_ParseTuple(args,"i:fact",&n))return NULL;//i 整形
?? ?result = factorial(n);
?? ?return Py_BuildValue("L",result);//L longlong型
}

static PyMethodDef wrapperMethods[] = 
{
?? ?{"fact",wrap_fact,METH_VARARGS,"Caculate N!"},//METH_NOARGS無(wú)需參數(shù)/METH_VARARGS需要參數(shù);
?? ?{NULL,NULL},
};
int initwrapper()
{
?? ?PyObject* m;
?? ?m = Py_InitModule("wrapper",wrapperMethods);//參數(shù):擴(kuò)展庫(kù)名稱/庫(kù)所包含的方法
?? ?return 0;
}

注:初始化函數(shù)名必須為initmodule_name這樣的格式

(2)安裝python-dev包含Python.h頭文件

安裝命令:sudo apt-get python-dev

(3)在linux環(huán)境下wrapper.c編譯成動(dòng)態(tài)鏈接庫(kù)wrapper.so

編譯命令:gcc wrapper.c -fPIC -shared -o wrapper.so -I/usr/include/python2.7 

注:雖然已經(jīng)安裝了python-dev,但編譯時(shí)仍然提示“Python.h:沒(méi)有那個(gè)文件或目錄”,需要通過(guò)gcc的-I dir選項(xiàng)在頭文件的搜索路徑列表中添加dir目錄

(4)Python文件中import wrapper導(dǎo)入動(dòng)態(tài)鏈接庫(kù),在import語(yǔ)句導(dǎo)入庫(kù)時(shí)會(huì)執(zhí)行初始化函數(shù)

(5)Python文件中wrapper.fact()方式對(duì)C函數(shù)調(diào)用時(shí),封裝函數(shù)wrap_fact()先會(huì)被調(diào)用,封裝函數(shù)接收到一個(gè)Python整形對(duì)象,PyArg_ParseTuple將Python整形對(duì)象轉(zhuǎn)為C整形參數(shù),然后調(diào)用C的factorial()函數(shù)并將C整數(shù)參數(shù)傳入,經(jīng)過(guò)運(yùn)算后得到一個(gè)C長(zhǎng)整形的返回值,Py_BuildValue把C長(zhǎng)整形返回值轉(zhuǎn)為Python的長(zhǎng)整形對(duì)象作為最終整個(gè)函數(shù)調(diào)用的結(jié)果。

(6)timeit測(cè)試函數(shù)wrapper.fact(20)執(zhí)行10000次所的時(shí)間只需要5.9ms。

factorial_rc 2432902008176640000 0.00598216056824

Python Ctypes模塊調(diào)用實(shí)現(xiàn)

Python內(nèi)建ctypes庫(kù)使用了各個(gè)平臺(tái)動(dòng)態(tài)加載動(dòng)態(tài)鏈接庫(kù)的方法,并在Python源生代碼基礎(chǔ)上通過(guò)類型映射方式將Python與二進(jìn)制動(dòng)態(tài)鏈接庫(kù)相關(guān)聯(lián),實(shí)現(xiàn)Python與C語(yǔ)言的混合編程,可以很方便地調(diào)用C語(yǔ)言動(dòng)態(tài)鏈接庫(kù)中的函數(shù)。(ctypes源碼路徑:/Modules/_ctypes/_ctypes.c、/Modules/_ctypes/callproc.c)

(1)將C編寫(xiě)的遞歸函數(shù)存為a.c,不需要對(duì)C函數(shù)經(jīng)過(guò)Python接口封裝

#include??? 
#include??? 
unsigned long long factorial(int n)
{
?? ?if(n<2)return 1;
?? ?return factorial(n-1)*n;
}

(2)在linux環(huán)境下a.c編譯成動(dòng)態(tài)鏈接庫(kù)a.so

編譯命令:gcc a.c -fPIC -shared -o a.so 

(3)Python文件中調(diào)用動(dòng)態(tài)鏈接庫(kù)a.so,在Windows平臺(tái)下,最終調(diào)用的是Windows API中LoadLibrary函數(shù)和GetProcAddress函數(shù),在Linux和Mac OS X平臺(tái)下,最終調(diào)用的是Posix標(biāo)準(zhǔn)中的dlopen和dlsym函數(shù)。ctypes庫(kù)內(nèi)部完成PyObject* 和C types之間的類型映射,使用時(shí)只需設(shè)置調(diào)用參數(shù)和返回值的轉(zhuǎn)換類型即可。

from ctypes import cdll
from ctypes import * 
libb = cdll.LoadLibrary("./a.so") 
def factorial_c(n):
?? ?return libb.factorial(n)

if __name__ == "__main__":
libb.factorial.restype=c_ulonglong#返回類型
    libb.factorial.argtype=c_int#傳入類型
print "factorial_c",factorial_c(20),timeit("factorial_c(20)","from __main__ import factorial_c",number=10000)

(4)timeit測(cè)試函數(shù)libb.factorial(20)執(zhí)行10000次所的時(shí)間需要8.5ms。

factorial_c 2432902008176640000 0.00857210159302

總結(jié)

如下圖所示,factorial、factorial_c、factorial_rc分別為Python腳本、ctypes 庫(kù)和Python源生代碼擴(kuò)展方式來(lái)實(shí)現(xiàn)函數(shù)的執(zhí)行時(shí)間。通過(guò)執(zhí)行時(shí)間對(duì)比可以發(fā)現(xiàn)調(diào)用C函數(shù)來(lái)擴(kuò)展Python功能可以大大提高執(zhí)行速度,而Python自帶的ctypes庫(kù)由于在源生代碼上進(jìn)行封裝,執(zhí)行時(shí)間會(huì)高于源生代碼擴(kuò)展方式,但ctypes庫(kù)使用方便,特別適合應(yīng)用在第三方封裝代碼,提供動(dòng)態(tài)鏈接庫(kù)和調(diào)用文檔的場(chǎng)合。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/41314.html

相關(guān)文章

  • 6個(gè)Python性能優(yōu)化技巧

    摘要:的批評(píng)者聲稱性能低效執(zhí)行緩慢,但實(shí)際上并非如此嘗試以下個(gè)小技巧,可以加快應(yīng)用程序。使用或者機(jī)器語(yǔ)言擴(kuò)展包來(lái)執(zhí)行關(guān)鍵任務(wù)能極大改善性能。但是如果你把求值的結(jié)果放入一個(gè)變量中,就能提高程序的性能。 Python是一門(mén)非??岬恼Z(yǔ)言,因?yàn)楹苌俚腜ython代碼可以在短時(shí)間內(nèi)做很多事情,并且,Python很容易就能支持多任務(wù)和多重處理。 Python的批評(píng)者聲稱Python性能低效、執(zhí)行緩慢,...

    RobinQu 評(píng)論0 收藏0
  • python大佬養(yǎng)成計(jì)劃----HTML網(wǎng)頁(yè)設(shè)計(jì)<二>

    摘要:但是語(yǔ)言并沒(méi)有成功,究其原因,認(rèn)為是其非開(kāi)標(biāo)識(shí)放造成的。已經(jīng)成為最受歡迎的程序設(shè)計(jì)語(yǔ)言之一。年月,該語(yǔ)言作者在郵件列表上宣布將于年月日終止支持。其中很重要的一項(xiàng)就是的縮進(jìn)規(guī)則。設(shè)計(jì)定位的設(shè)計(jì)哲學(xué)是優(yōu)雅明確簡(jiǎn)單。 文本標(biāo)簽 換行標(biāo)簽 -- br 是單標(biāo)簽,意味著它沒(méi)有結(jié)束標(biāo)簽。起強(qiáng)制換行作用 段落中的文字段落中的文字段落中的文字 水平分割線 -- hr 與br相同,也是單標(biāo)簽??捎脕?lái)區(qū)分...

    jiekechoo 評(píng)論0 收藏0
  • SegmentFault 技術(shù)周刊 Vol.40 - 2018,來(lái)學(xué)習(xí)一門(mén)新編程語(yǔ)言吧!

    摘要:入門(mén),第一個(gè)這是一門(mén)很新的語(yǔ)言,年前后正式公布,算起來(lái)是比較年輕的編程語(yǔ)言了,更重要的是它是面向程序員的函數(shù)式編程語(yǔ)言,它的代碼運(yùn)行在之上。它通過(guò)編輯類工具,帶來(lái)了先進(jìn)的編輯體驗(yàn),增強(qiáng)了語(yǔ)言服務(wù)。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺(jué)已經(jīng)到來(lái)了,總結(jié)過(guò)去的 2017,相信小伙們一定有很多收獲...

    caspar 評(píng)論0 收藏0
  • SegmentFault 技術(shù)周刊 Vol.40 - 2018,來(lái)學(xué)習(xí)一門(mén)新編程語(yǔ)言吧!

    摘要:入門(mén),第一個(gè)這是一門(mén)很新的語(yǔ)言,年前后正式公布,算起來(lái)是比較年輕的編程語(yǔ)言了,更重要的是它是面向程序員的函數(shù)式編程語(yǔ)言,它的代碼運(yùn)行在之上。它通過(guò)編輯類工具,帶來(lái)了先進(jìn)的編輯體驗(yàn),增強(qiáng)了語(yǔ)言服務(wù)。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺(jué)已經(jīng)到來(lái)了,總結(jié)過(guò)去的 2017,相信小伙們一定有很多收獲...

    nihao 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<