四时宝库

程序员的知识宝库

「 C 」 将C代码封装成python可以调用的so

在模型落地阶段,或者在深度学习的训练框架中,比如pytorch, 通常需要用C完成数据结构,多线程多GPU计算部分,python只是调用C的数据结构。

也就是说:用C写功能,用python调用。

  1. 编写文件pysample.c,内容如下:
Bash
/* 定义普通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,内容如下:

Bash
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 运行如下命令:

Bash
 python3 buildlib.py build_ext --inplace 

会生成:

Bash
	sample.cpython-39-darwin.so

4 打开python,输入:

Bash
import sample
sample.add(1,1)

得:


将C编译成so,然后用python调用,成功。

总结:这里使用了PyMethodDef方法定义,PyModuleDef模块定义以及PyModuleCreate函数来生成sample.cpython-39-darwin.so, 在本so所在文件夹下,import sample即可使用所定义的函数:

注意:编译的过程中,可能会有错误,耐心处理一下编译错误。

比如,我这里就出现如下错误:


Bash
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删除,如下图:

Bash
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添加运行权限

Bash
 chmod +x build.sh

3 运行:

Bash
./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.

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接