Python筆記 外部c函式呼叫

2021-10-07 01:51:52 字數 4791 閱讀 8230

前些時候,乙個朋友突然問我: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 中所表示的...