動態載入C library (DLL)
從第一次知道python這個語言時,就聽聞python的其中一項強大之處,高彈性:
直接利用python coding方便又快速,真的遇到關鍵的效能瓶頸,再以C語言改寫即可。
CPython能達到這項目的的方法,個人知道有2種:Python/C API和ctypes.
P.S. 當然,如果目的是加速或與其他語言做binding就不只這兩項了。
例如RPython, Cython, …等派生或是擴充語言,但那進來考慮實在有點消化不完。
一來目前主要是想摸熟python本身的特性,
二來由於目前個人感興趣的部分在於CPython和C如何直接溝通,因此其他方式暫時不考慮了。
ctypes
最後稍微比較過後,Python及C之間的API也決定暫時擱置。
以之前經驗來說,這類的API接口大多只要搞懂制式的規格和作用,
剩下來的就是類似將內容填入表格的繁瑣工作。
相較之下,若能在幾乎不動C source code的情形下做到這件事情,個人認為有很大的優勢。
以下是一個用C語言寫成簡單(幾乎無經過修飾)的開方根的程式:
/* File: c_sqrt.c */
doublesqrt(doublek) {
int i;
double x=k;
for(i=0; i<5; i++)
x = x - (x*x-k)/(2*x); // Newton's Iteration.
return x;
}
接著把這個compile成shared library.
$ gcc c_sqrt.c -shared -fPIC -O2 -oc_sqrt.so
需要注意的是,由於個人的電腦都是Linux/OS X等類Unix系的,所以動態函式庫是.so檔。
而在python中,則直接利用ctypes module來讀檔:
import ctypes
dyn = ctype.CDLL('./c_sqrt.so')
dyn.sqrt.argtypes = [ctypes.c_double] # default: None
dyn.sqrt.restype = ctypes.c_double # default: None
# default: c_int
print( dyn.sqrt(5) )
這段程式造出一個CDLL的物件dyn。
利用dyn去讀取sqrt時,會到c_sqrt.so找到sqrt的symbol,然後加到dyn這個物件裡。
然而預設的情況下,一個由CDLL讀入的C函數是無參數、回傳值為整數的函數。
如果直接呼叫dyn.sqrt(5)也只會得到0而已。
必須修改傳入的參數的型別(如第四行那樣,有多少參數就寫進list裡),
restype則是回傳的型別。
若回傳的值是一個結構包裝的東西,
或許還得要再經過宣告一個新的類別來使用:
from ctypes import *
class foo(Structure):
_fields_=[("val", c_int), ("dat", POINTER(c_int))]
參考資料:
- Python Document: ctypes — A foreign function library for Python
- 程式設計遇上小提琴 - Python的進步: ctypes
- (參考) Dynamically Loaded (DL) Libraries ,看看C語言中怎麼做到上面的事情.
沒有留言:
張貼留言