前些時候,乙個朋友突然問我:python做計算實在是太慢了,有什麼辦法可以加速python的運算嗎?我說:簡單啊,你直接呼叫外部c函式就行了,我印象中cython可以直接實現的。聞言,我那個朋友喜出望外,遂言:太好了,那你給我寫個demo唄。。。
emmmm。。。
好吧,我承認我之前事實上只是知道可以這麼做,真的要說實現。。。
唉,自己挖的坑,流著淚也要把它填平了。於是,趁著週末兩天,我網上找了一些demo,然後自己實現了幾種python呼叫外部c函式的實現方式。
不要問我為啥今天才發出來,問就是打字慢。
下面,話不多說,上乾貨!
c_types實現大約是最簡單的外部c函式實現方法了,你只需要準備寫好你的c函式實現,然後編譯,最後呼叫就行了,無需任何中間檔案,一切都是如此簡單。
下面,我們以a到b的連續求和函式作為例子來進行說明:
step 01: 寫作c**實現
我們在my_func.c
指令碼中實現我們的**如下:
int myfunc(int a, int b)
return ans;
}
step 02: 編譯指令碼
我們呼叫如下命令即可:
gcc -shared -o mylib.so myfunc.c
step 03: 呼叫測試
最後,我們只要在python**中使用如下方法呼叫即可:
from ctypes import cdll
mylib = cdll(
"./mylib.so"
)mylib.myfunc(0,
100)
# 5050
完事,收工,一切都是如此順利!
較之ctypes實現方法,cython方法會更加複雜一點,它不需要依賴於ctypes庫,而是直接將c**轉譯為python底層c實現中可讀的**,而後將這一部分封裝為乙個動態鏈結庫。
因此,在這種情況下,我們完全可以將這個生成的動態鏈結庫當成乙個普通的python包來進行呼叫,其執行效率上也會優於ctypes方式的呼叫。
下面,我們來考察其具體實現。
step 01: 準備c函式實現
這部分完全同上,這裡不再贅述。
step 02: 配置動態鏈結庫
我們在外準備乙個mylib.pyx
檔案用於配置我們需要封裝的動態鏈結庫。
cdef extern from
"myfunc.c"
: cpdef int myfunc(
int a,
int b)
step 03: 動態鏈結庫生成
在準備好了動態鏈結庫配置檔案之後,我們構建乙個setup.py
檔案來生成我們的動態鏈結庫。
setup.py
檔案內容如下:
from distutils.core import setup, extension
from cython.build import cythonize
ext_modules=
[ extension(
"mylib"
, sources=
["mylib.pyx"])
]setup(
name =
, ext_modules = cythonize(ext_modules)
)
而後,我們呼叫下述命令進行呼叫:
python setup.py build_ext --inplace
操作完成之後,我們即可得到乙個.so
動態鏈結庫檔案,該檔案即為我們可呼叫的python包。
step 04: 呼叫測試
我們使用如下**即可進行呼叫測試。
from mylib import myfunc
myfunc(0,
100)
# 5050
由是,cython方法搞定收工!
注意到,cython方式構建動態鏈結庫過程中,會呼叫cythonize
函式,而這個函式會先生成乙個.c
中間檔案,而這個中間檔案即為我們的動態鏈結庫中真實包含的c函式**實現。
事實上,後續的setup函式就是針對這個.c
中間檔案進行編譯並構建為動態鏈結庫。
因此,我們可以繞過cythonize
函式,直接自己來構建這個.c
檔案,然後進行動態鏈結庫的構建。
而這,就是c extension方法的主要思路。
同樣的,給出其操作流程如下:
step 01: c函式準備
這部分內容見上即可。
step 02: 配置動態鏈結庫
// mylib.c
#include #include "myfunc.c" // 自定義c函式庫,包含目標函式myfunc
// 轉義c函式
static pyobject * demo(pyobject *self, pyobject *args)
// 定義動態鏈結庫中包含的函式
static pymethoddef demomethods = ,
};// 定義動態鏈結庫
static struct pymoduledef cmodpydem =
;// 構建動態鏈結庫函式介面,後續的setup.py指令碼將會呼叫這一函式進行生成
pymodinit_func pyinit_mylib(void)
step 03: 動態鏈結庫構建
動態鏈結庫的構建方法與上述cython方法相仿,我們只需要給出乙個setup.py
指令碼並進行呼叫即可。
給出其內容如下:
from distutils.core import setup, extension
module = extension(
"mylib"
, sources=
["mylib.c"])
setup(name =
"build my c library"
, ext_modules=
[module]
)
而後,我們同樣採用下述命令編譯即可:
python setup.py build_ext --inplace
step 04: 呼叫測試
由於這一方式與上述cython方式本質上是完全相同的,因此,其呼叫方法也完全相同,我們對此不再贅述。
綜上,c extension方法搞定!
swig也是常用的python呼叫外部c函式的實現方法之一,其核心與上述cython完全相似,唯一的區別點在於,cython方法使用cython庫來進行**轉義,而這裡使用swig進行**轉義。。。
我們給出其操作流程如下:
step 01: c函式實現
bala,bala,bala。。。
step 02: 配置動態鏈結庫
%module mylib
%int myfunc(int a, int b);
swig -python mylib.i
step 03: 構建動態鏈結庫
在上述步驟後,我們會得到乙個名為mylib_wrap.c
的檔案,而後,我們仿照c extension方法給出下述構建指令碼setup.py
即可:
from distutils.core import setup, extension
module =
[ extension(
'mylib'
, sources=
['myfunc.c'
,'mylib_wrap.c'])
]setup (name =
'mylib'
, ext_modules = module)
python setup.py -build_ext --inplace
step 04: 呼叫測試
da…da…da…
至此,swig方法搞定!
現在,我們來比較一下上述各個方法呼叫外部c函式的效能。
我們重複執行107
10^7
107次計算1到100的連續求和,得到各個方法的耗時如下:
方法耗時
python
~50s
ctypes
~7scython
1.88s
c extension
2.76s
swig
2.41s
結論:上述4種方式實現c函式外部呼叫確實能給python帶來極大的效能提公升;
就實現方式來說,ctypes是最容易實現的,但是相對的,其執行效率也是4種方法中最慢的;
c extension、cython以及swig三種實現方法本質上來說是同一種實現方法,其外部c函式呼叫的執行速度上沒有量級上的差異,但是從其實際的效果來看,cython方式相對而言操作更為簡單,其效率也是最高的。
[1] 在python裡呼叫c函式的三種方式
[2] python呼叫c和c++庫(直接呼叫和使用swig)
[3] swig and python
Python學習筆記之呼叫外部函式並寫入到本地檔案
桌面上的第乙個txt from abstest import my abs a my abs 99 print a f open r c users administrator desktop test.txt w f.write str my abs 99 f.close 首行 呼叫了abstes...
外部函式的呼叫
如計算下列排列函式 先建立乙個原始檔如內部函式1 includeusing namespace std int factorial int n return m 這個被呼叫的函式的主函式不是int main 而是自己定義的函式 在建立乙個內部函式2 includeusing namespace st...
C 呼叫外部程式
關於三個sdk函式 winexec,shellexecute,createprocess的其他注意事項 定義標頭檔案 必須定義以下兩個標頭檔案 include 可替換為 windows.h include如果定義了標頭檔案 include 的話就不必定義 include 了。定義路徑 c 中所表示的...