很多時候,我們是基於python進行模型的設計和運行,可是基於python本身的速度問題,使得原生態python代碼無法滿足生產需求,不過我們可以借助其他編程語言來緩解python開發的性能瓶頸。這里簡單介紹個例子,以此完成如何先基於cuda編寫瓶頸函數,然后在將接口通過cpp進行封裝,最后以庫的形式被python調用。
1 cpp+python
首先,介紹下如何python調用cpp的代碼。這里極力推薦pybind11。因為pybind11是一個輕量級,只包含頭文件的庫,他可以在C++中調用python,或者python中調用C++代碼。其語法類似Boost.Python。可是不同的是Boost是一個重量級的庫,因為為了兼容幾乎所有的C++編譯器,所以需要支持哪些最老的,bug最多的編譯器。該作者考慮到現在c11都很普及了,所以丟棄那些之前的東西,從而打造這么一個輕量級的庫。我們通過代碼統計:
首先是對pybind11的安裝:
git clone https://github.com/pybind/pybind11.git
cd pybind11
mkdir build && cd build
cmake ../
make -j32
上述cmake需要3.2及以上版本。最后輸出結果如下圖所示:
這里簡單呈現下一級目錄:
為了實現python調用cpp,我們先建立個文件名叫test.cpp
#include<pybind11/pybind11.h>
namespace py = pybind11;
int
add(int i, int j){
return i+j;
}
// 該宏會在python的import語句觸發
PYBIND11_MODULE(example, m){
m.doc() = "pybind11 example plugin";
m.def("add", &add, "a function which adds two numbers",
py::arg("i"), py::arg("j"));
}
然后執行:
g++ -Wall -shared -std=c++11 -fPIC \
-I/home/zzc/software/pybind11/include \
`cd /home/zzc/software/pybind11 && python3 -m pybind11 --includes` \
test.cpp \
-o example`python3-config --extension-suffix`
結果如下圖
接下來,我們將其改成參數支持numpy,可參考官網文檔;pybind11—python numpy與C++數據傳遞:
#include<pybind11/pybind11.h>
#include<pybind11/numpy.h>
namespace py = pybind11;
int
add(py::array_t<float> &array, int col){
py::buffer_info buf1 = array.request();
float *p = (float *)buf1.ptr;
for (int i=0; i<col; i++){
printf("cur value %lf\n", *p++);
}
return 0;
}
PYBIND11_MODULE(example, m){
m.doc() = "pybind11 example plugin";
m.def("add", &add, "a function which adds two numbers");
}
然后依然用上述命令編譯成so,調用結果如下圖:
更詳細的pybind11使用方法,可閱讀官方文檔
2 cuda+cpp+python
這里只介紹如何編寫cuda的代碼,然后提供python接口。通過調查pybind11的issues:alias template error with Intel 2016.0.3 compilers,如果直接編寫cu代碼,然后一步到位,會觸發很多問題。而如這里最后所述,較好的方式就是分開:
- 編寫cuda代碼,並生成動態鏈接庫;
- 編寫cpp代碼,通過函數引用方式用pybind11進行接口封裝;
- python導入對應模塊即可使用。
如上圖所示,首先,編寫cuda代碼,這里為了簡潔,我們只寫一個printf
// cuda_test.cu
#include<cuda_runtime.h>
#include<stdio.h>
__global__ void
kernel(){
printf("inside in kernel\n");
}
int
cuda(int a, int b){
kernel<<<1,10>>>();
cudaDeviceSynchronize();
return 0;
}
對應頭文件:
//cuda_test.h
int cuda(int, int);
然后我們將其用nvcc編譯成動態鏈接庫
nvcc --shared -Xcompiler -fPIC cuda_test.cu -o libcutest.so
結果如上圖
接着,我們借助pybind11,此時增加了幾行
#include<pybind11/pybind11.h>
#include"cuda_test.h" //新增的
namespace py = pybind11;
int
add(int i, int j){
return i+j;
}
PYBIND11_MODULE(example, m){
m.doc() = "pybind11 example plugin";
m.def("add", &add, "a function which adds two numbers",
py::arg("i"), py::arg("j"));
m.def("cuda", &cuda,"testing",
py::arg("a"), py::arg("b")); //新增的
}
然后輸入如下編譯方式:
g++ -Wall -shared -std=c++11 -fPIC \
-L. -lcutest \
-I/home/zzc/software/pybind11/include \
`cd /home/zzc/software/pybind11 && python3 -mpybind11 --includes` \
test.cpp \
-o example`python3-config --extension-suffix`
此時生成結果
然后使用