python源码编译ffi.h⽂件⽆路径_三、faster-rcnn源码阅读:
CC++CU。。。
作为⼀个copyer,下载了别⼈的代码⾸先就是要编译⼀些扩展库,如果编译不成功,就运⾏不了,⽤别⼈的代码都不会,实在不是⼀个称职的copyer,所以还是决⼼把各种扩展⽅法学习⼀下。
由于设计到很多办法,其中⼜有很多细节,所以这篇笔记特别的冗杂,前⽅⾼能,⼩⼼驾驶。
前⾯读了两个cu(CUDA)⽂件,要把这些扩展让python能够调⽤,还需要编译安装,这个过程⽅法多种多样,花样繁多,每个源码库采⽤的编译和安装⽅式都不⼀样。不过最终基本都是要求执⾏类似这样的命令:
python tup.py build_ext --inplace
#或sh make.sh
运⽓好的话就能成功编译了,运⽓不好的话会报⼀堆的错误,可能需要做⼀定的修改,要么改环境,要么改代码,要么重新找⼀份能编译通过的源码,因此了解⼀下编译安装c扩展的基本知识还是有必要的。
⼀、原始的⽅式
其他⽅式都是对原始⽅式的包装简化,所以先了解⼀下原始的⽅式是啥样⼦。
⽐如⽤C实现了⼀个函数:
int add(int a,int b) {
return a + b;
}
这个函数python直接是⽤不了的,需要进⾏⼀系列的包装:
//test.c//1,这个头⽂件必须要的#include "Python.h"//2, 函数主体int add(int a,int b) {
return a + b;
}
//3, 包裹函数static PyObject *Exten_add(PyObject *lf,PyObject *args) {
int a,b;
/
/ 获取数据,i代表int,ii代表两个int // 如果没有获取到,则返回NULL if (!PyArg_ParTuple(args,"ii",&a,&b)) {
return NULL;
}
return (PyObject*)Py_BuildValue("i",add(a,b));
}
//4, 添加PyMethodDef ModuleMethods[]数组static PyMethodDef ExtenMethods[] = {
cdc是什么缩写
// add:可⽤于Python调⽤的函数名,Exten_add:C++中对应的函数名 {"add",Exten_add,METH_VARARGS},
{NULL,NULL},
};
//5, 初始化函数static struct PyModuleDef ExtenModule = {
"Exten",//模块名称 NULL,
-1,
ExtenMethods
};
//6.初始化模块//注意:函数名称PyInit_Exten要与模块名称Exten匹配void PyInit_Exten() {
PyModule_Create(&ExtenModule);
}
还么结束,还需要写⼀个tup.py⽂件:
#tup.py
import tup,Extension
#这⾥改为from tuptools import tup, Extension也可以,通常没什么区别
#distutils是python标准库的⼀部分,tuptools是distutils的增强版。具体我也不清楚,得看⽂档吧
MOD = 'Exten' #模块名
tup(name=MOD,ext_modules=[Extension(MOD,sources=['test.c'])])
现在只要执⾏
python tup.py build
就编译好了,可以⽤⼀段代码测试⼀下
#test.pyglobal forest watch
trick or treat是什么意思
百度翻译在线翻译英语import Exten
print(Exten.add(1,3))
但是,说实在话,这样的代码看着有点让⼈难受,编程本来是⼀件⾮常美好的事情。。。
cult of personality
⽽且,C++是调⽤不了的,要调⽤C++,还得加⼀层C包装函数。。。
所以就有了很多的种⽅式来简化包装过程。下⾯介绍⼏种我看到的⽅式(都是在不同版本的faster-rcnn源码库中看到的)
⼆、torch.utils.ffi
这种⽅式是pytorch实现的,优点是可以⽤pytorch的底层aten库,与pytorch深度融合,缺点只能在0.4中使⽤,到了pytorch1.0就已经废弃了,被torch.utils.cpp_extension取代。
三、torch.utils.cpp_extension
这种⽅式实现的优点是包装了C++/cuda,在C++代码⾥⽤pybind11把C++包装成C接⼝,不⽤再为C++发愁,⽽且与pytorch深度融合,让代码与标准库⽆异,⽐如⾃动微分。所以这是pytorch推荐的⽅式。
看看pybind11如何包装C++接⼝的吧:
//vision.cpp// Copyright (c) Facebook, Inc. and its affiliates. All Rights Rerved.#include "nms.h"#include
"ROIAlign.h"#include "ROIPool.h"#include "SigmoidFocalLoss.h"#include "deform_conv.h"#include "deform_pool.h"
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
m.def("nms", &nms, "non-maximum suppression");
m.def("roi_align_backward", &ROIAlign_backward, "ROIAlign_backward");
m.def("roi_pool_forward", &ROIPool_forward, "ROIPool_forward");
m.def("roi_pool_backward", &ROIPool_backward, "ROIPool_backward");
m.def("sigmoid_focalloss_forward", &SigmoidFocalLoss_forward, "SigmoidFocalLoss_forward");
m.def("sigmoid_focalloss_backward", &SigmoidFocalLoss_backward, "SigmoidFocalLoss_backward");
// dcn-v2 m.def("deform_conv_forward", &deform_conv_forward, "deform_conv_forward");
m.def("deform_conv_backward_input", &deform_conv_backward_input, "deform_conv_backward_input");
m.def("deform_conv_backward_parameters", &deform_conv_backward_parameters, "deform_conv_
backward_parameters"); m.def("modulated_deform_conv_forward", &modulated_deform_conv_forward, "modulated_deform_conv_forward");
m.def("modulated_deform_conv_backward", &modulated_deform_conv_backward, "modulated_deform_conv_backward"); m.def("deform_psroi_pooling_forward", &deform_psroi_pooling_forward, "deform_psroi_pooling_forward");
m.def("deform_psroi_pooling_backward", &deform_psroi_pooling_backward, "deform_psroi_pooling_backward");
}
是不是优雅了很多?这14个函数如果⽤原始⽅式包装得该有多难看。
再写⼀个极简的玩具例程:
1、在头⽂件test.h中定义⼀个函数add
//这⾥#include 是必须的,⾥⾯包含了pybind11,还有pytorch的基础库aten等等
#include
int add(int a,int b);
2、test.cpp中实现这个函数,并⽤pybind11包装接⼝
//这⾥可以加#include "test.h",也可以不要
int add(int a,int b) {
return a + b;
}
3、写⼀个包装⽂件vision.cpp
#include "test.h"
//包装接⼝
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
m.def("add", &add, "TEST toy");
}
当然,2和3可以放进⼀个⽂件⾥,不过分开写更模块化⼀些
4、tup.py⽂件中⽤tuptools和torch.utils.cpp_extension写编译安装过程
irs
from tuptools import tup
tup(
name='test_cpp', # 模块名称,需要在python中调⽤
version="0.1",
ext_modules=[
CppExtension('test_cpp', sources=["test.cpp"], include_dirs=["."]),
],
cmdclass={
hac
'build_ext': BuildExtension
}
)
完成!Great,⾮常简洁!
只需要执⾏
python tup.py build_ext --inplace
写⼀个测试代码看看
#注意:如果#include ,
#那么在使⽤的时候,先import torch,
#否则在cpp与cu混编的时候import test_cpp会失败
import torch
import test_cpp
print(test_cpp.add(1,2))
pybind11本⾝是独⽴于pytorch的,ertorch.utils.cpp_extension中的BuildExtension, CppExtension,CUDAExtension帮助我们⽣成扩展代码,不需要处理复杂的编译命令,环境变量,源⽂件⽬录,头⽂件⽬录,库⽂件⽬录,gcc、nvcc编译选项……⼀切都有了默认配置(⾃⼰配置也可以),⽤起来特别爽!关键是——注意到没有,这还是个C++⽂件⽽不是C⽂件!
四、纯pybind11:
纯pybind11没看到哪个实现⾥⽤,不过pybind11实在太好了,所以这⾥按照⽂档写⼀个helloword:
如果没有安装pybind11,就先:
pip install pybind11
1.写⼀个example.cpp ⽂件:
#include
int add(int i, int j) {
海外看中国军事return i + j;
}
PYBIND11_MODULE(example, m) {
m.doc() = "pybind11 example plugin"; // optional module docstring m.def("add", &add, "A function which adds two
}
2.编译:
c++ -O3 -Wall -shared -std=c++11 -fPIC `python3 -m pybind11 --includes` example.cpp -o example`python3-config --extension-suffix`
//windows:nvcc --shared example.cpp -o example.pyd -I D:\Miniconda3\include
My GOD!就这样成功了!
测试⼀个:
import example
print(example.add(1, 2))
OK,没问题,这也太爽了吧!
与cuda混合编程:
7年级下册英语⽂档):
1、test.h:头⽂件
#include #include #include #include
using std::vector;
namespace py = pybind11;
py::array_t add(py::array_t a_h,py::array_t b_h);
美容护肤培训
2、sum_arrays.cu:两个整数数组相加
#include #include #include "test.h"#define CHECK(call)\{\const cudaError_t error=call;\if(error!=cudaSuccess)\
{\printf("ERROR: %s:%d,",__FILE__,__LINE__);\printf("code:%d,reason:%s\n",error,cudaGetErrorString(error));\exit(1);\}\} __global__ void sumArraysGPU(int*a,int*b,int*res,int nElem)
{
int i=blockIdx.x*blockDim.x+threadIdx.x;
if(i
{
res[i]=a[i]+b[i];
}
}
py::array_t add(py::array_t a_h,py::array_t b_h)
{
py::buffer_info buf1 = quest();
py::buffer_info buf2 = quest();
int nElem=buf1.shape[0];
int nByte=sizeof(int)*nElem;