welcom ! Handel home

2024年12月2日 星期一

Python 載入 gcc DLL (so) Note

 

動態載入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))]

參考資料:

  1. Python Document: ctypes — A foreign function library for Python
  2. 程式設計遇上小提琴 - Python的進步: ctypes
  3. (參考) Dynamically Loaded (DL) Libraries ,看看C語言中怎麼做到上面的事情.

參考自  ==> 
https://www.gitbook.com/?utm_source=legacy&utm_medium=redirect&utm_campaign=close_legacy

沒有留言: