在模型落地阶段,或者在深度学习的训练框架中,比如pytorch, 通常需要用C完成数据结构,多线程多GPU计算部分,python只是调用C的数据结构。
也就是说:用C写功能,用python调用。
- 编写文件pysample.c,内容如下:
/* 定义普通C语言实现的add() */
int add(int a,int b)
{
return a+b;
}
/* 把普通C语言实现的add()封装成Python可以调用的函数 */
static PyObject *py_add(PyObject *self, PyObject *args) {
int x, y, result;
/* 从 args 里解析实际参数 */
if (!PyArg_ParseTuple(args,"ii", &x, &y)) {
return NULL;
}
/* 调用普通C语言实现的add() */
result = add(x,y);
/* 把 int 转化为 PyObject* */
return Py_BuildValue("i", result);
}
/* 定义模块的 method table */
static PyMethodDef SampleMethods[] = {
{"add", py_add, METH_VARARGS, "Greatest common divisor"},
{ NULL, NULL, 0, NULL}
};
/* 定义模块结构 */
static struct PyModuleDef samplemodule = {
PyModuleDef_HEAD_INIT,
"sample", /* name of module */
"A sample module", /* Doc string (may be NULL) */
-1, /* Size of per-interpreter state or -1 */
SampleMethods /* Method table */
};
/* 模块初始化函数 */
PyMODINIT_FUNC PyInit_sample(void) {
return PyModule_Create(&samplemodule);
}
2 编写buildlib.py,内容如下:
from distutils.core import setup, Extension
setup(name='sample',
ext_modules=[
Extension('sample',
['pysample.c'],
include_dirs = ['/some/dir'],
define_macros = [('FOO','1')],
undef_macros = ['BAR'],
library_dirs = ['/usr/local/lib'],
libraries = ['sample']
)
]
)
3 运行如下命令:
python3 buildlib.py build_ext --inplace
会生成:
sample.cpython-39-darwin.so
4 打开python,输入:
import sample
sample.add(1,1)
得:
将C编译成so,然后用python调用,成功。
总结:这里使用了PyMethodDef方法定义,PyModuleDef模块定义以及PyModuleCreate函数来生成sample.cpython-39-darwin.so, 在本so所在文件夹下,import sample即可使用所定义的函数:
注意:编译的过程中,可能会有错误,耐心处理一下编译错误。
比如,我这里就出现如下错误:
zhaomingming@localhost test % python3 buildlib.py build_ext --inplace
running build_ext
building 'sample' extension
creating build
creating build/temp.macosx-10.15-x86_64-3.9
clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk -I/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/include -I/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/Tk.framework/Versions/8.5/Headers -DFOO=1 -UBAR -I/some/dir -I/usr/local/include -I/usr/local/opt/openssl@1.1/include -I/usr/local/opt/sqlite/include -I/usr/local/Cellar/python@3.9/3.9.0_1/Frameworks/Python.framework/Versions/3.9/include/python3.9 -c pysample.c -o build/temp.macosx-10.15-x86_64-3.9/pysample.o
clang -bundle -undefined dynamic_lookup -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk build/temp.macosx-10.15-x86_64-3.9/pysample.o -L/usr/local/lib -L/usr/local/lib -L/usr/local/opt/openssl@1.1/lib -L/usr/local/opt/sqlite/lib -lsample -o /Users/zhaomingming/Downloads/test_c2python/test/sample.cpython-39-darwin.so
ld: library not found for -lsample
clang: error: linker command failed with exit code 1 (use -v to see invocation)
error: command '/usr/bin/clang' failed with exit code 1
我的解决办法:
1 新建build.sh文件,将clang对应的两句命令写入,但是把-lsample删除,如下图:
clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk -I/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/include -I/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/Tk.framework/Versions/8.5/Headers -DFOO=1 -UBAR -I/some/dir -I/usr/local/include -I/usr/local/opt/openssl@1.1/include -I/usr/local/opt/sqlite/include -I/usr/local/Cellar/python@3.9/3.9.0_1/Frameworks/Python.framework/Versions/3.9/include/python3.9 -c pysample.c -o build/temp.macosx-10.15-x86_64-3.9/pysample.o
clang -bundle -undefined dynamic_lookup -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk build/temp.macosx-10.15-x86_64-3.9/pysample.o -L/usr/local/lib -L/usr/local/lib -L/usr/local/opt/openssl@1.1/lib -L/usr/local/opt/sqlite/lib -o /Users/zhaomingming/Downloads/test_c2python/test/sample.cpython-39-darwin.so
2 然后给build.sh添加运行权限
chmod +x build.sh
3 运行:
./build.sh
就会看到sample.cpython-39-darwin.so文件了。
观察pytroch源码:
可以看到,pytorch使用PyModuleDef定义了3个Module,一个torch._dl,一个torch._C和torch._C._fx,
其中torch._C 这个里面封装了所有的用C写的数据结构Tensor,edge,Node,function等.
pytorch的源码的迷惑点之一就是这里C与python代码的沟通问题,对C与python代码的沟通办法了解之后, 再看pytorch源码,以及某些部署框架的源码就会清晰起来:
原来框架中主要用c/c++实现基本数据结构和相关算子,比如卷积,然后用python实现更高级的封装。这样就保证让计算量大的部分交给C,封装繁琐的部分交给python.