成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

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

MudOnTire / 858人閱讀

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

前言

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


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

使用Python編寫一個遞歸函數(shù)和循環(huán)函數(shù),應(yīng)用Python的計(jì)時庫timeit測試函數(shù)執(zhí)行10000次所需要的時間分別為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)簡潔,但由于它逐層深入調(diào)用的機(jī)制使得執(zhí)行效率不如循環(huán),以下的測試可以發(fā)現(xiàn)每一次遞歸是新一次的函數(shù)調(diào)用,會產(chǎn)生新的局部變量,增加了執(zhí)行時間。但是即使使用For循環(huán)實(shí)現(xiàn)也需要41ms時間,接下來我們嘗試更快的實(shí)現(xiàn)方式。

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

(1)將C編寫的遞歸函數(shù)存為wrapper.c,作為Python的擴(kuò)展庫。同時需要對C函數(shù)增加一個型如PyObject* Module_func()的封裝接口,該接口用于Python解釋器的交互。將封裝接口加入至型如PyMethodDef ModuleMethods[]的數(shù)組中,Python解釋器能夠從數(shù)組中導(dǎo)入并調(diào)用到封裝接口。最后是實(shí)現(xiàn)對擴(kuò)展庫的初始化函數(shù),調(diào)用Py_InitModule()函數(shù),把擴(kuò)展庫和ModuleMethods[]數(shù)組的名字傳遞進(jìn)去,以便于解釋器能正確的調(diào)用庫中的函數(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無需參數(shù)/METH_VARARGS需要參數(shù);
?? ?{NULL,NULL},
};
int initwrapper()
{
?? ?PyObject* m;
?? ?m = Py_InitModule("wrapper",wrapperMethods);//參數(shù):擴(kuò)展庫名稱/庫所包含的方法
?? ?return 0;
}

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

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

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

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

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

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

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

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

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

factorial_rc 2432902008176640000 0.00598216056824

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

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

(1)將C編寫的遞歸函數(shù)存為a.c,不需要對C函數(shù)經(jīng)過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編譯成動態(tài)鏈接庫a.so

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

(3)Python文件中調(diào)用動態(tài)鏈接庫a.so,在Windows平臺下,最終調(diào)用的是Windows API中LoadLibrary函數(shù)和GetProcAddress函數(shù),在Linux和Mac OS X平臺下,最終調(diào)用的是Posix標(biāo)準(zhǔn)中的dlopen和dlsym函數(shù)。ctypes庫內(nèi)部完成PyObject* 和C types之間的類型映射,使用時只需設(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測試函數(shù)libb.factorial(20)執(zhí)行10000次所的時間需要8.5ms。

factorial_c 2432902008176640000 0.00857210159302

總結(jié)

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

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

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/41314.html

相關(guān)文章

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

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

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

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

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

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

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

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

    nihao 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<