|
| 1 | +# Paddle多语言接口实现 |
| 2 | +## 背景 |
| 3 | + |
| 4 | +Paddle需要一个多语言接口,这个接口需要做到: |
| 5 | + |
| 6 | +* 有标准的,良好的文档 |
| 7 | + * 例如Python可以使用[Sphinx](http://www.sphinx-doc.org/en/stable/)生成API文档,golang可以使用[GoDoc](https://godoc.org/golang.org/x/tools/cmd/godoc)生成文档。这都需要这个接口按照约定俗成的规则来注释完备。 |
| 8 | +* 不同语言的接口适应不同语言的特性 |
| 9 | + * 例如Java与Python的错误处理是直接扔出来Exception,而对于golang错误处理应该使用返回值。 |
| 10 | + |
| 11 | +我们可以将计算相关的代码使用C/C++来完成,而将逻辑结构相关的代码交由其他更高级的语言完成。不同开发语言具有不同的优势领域。使用多种语言混合开发Paddle可以增加Paddle的开发效率。 |
| 12 | + |
| 13 | + |
| 14 | +## 基本要求 |
| 15 | + |
| 16 | +Paddle的多语言接口实现包括一下几个方面: |
| 17 | + |
| 18 | +* 我们使用动态库来分发Paddle。在这个动态库中不嵌入任何其他语言的解释器,也不使用其他动态库。 |
| 19 | +* 这个动态库使用C99标准的头文件导出一些函数,不使用/导出C++符号。 |
| 20 | +* 不导出Paddle内部的结构体、类,仅仅使用`void*`指针作为类型的句柄(handler)。 |
| 21 | +* 不使用SWIG这种代码生成器,而是手写多语言绑定。 |
| 22 | + |
| 23 | + |
| 24 | +## 原因 |
| 25 | + |
| 26 | +### 使用动态库来分发Paddle |
| 27 | + |
| 28 | +* Paddle的链接方式比较复杂(使用了--whole-archieve),如果使用了静态库分发,那么要求使用Paddle库的人按照Paddle的要求链接Paddle,这通常比较复杂。 |
| 29 | +* 编译型语言,例如C/C++使用静态库和动态库难度差不多。但是含有解释器的语言,例如python或者java,基本上只能够调用动态库。 |
| 30 | + |
| 31 | +### 动态库中不嵌入任何其他语言的解释器 |
| 32 | + |
| 33 | +* 目前Paddle的进程模型是C++内部驱动Python解释器进行模型配置解析和数据读取 |
| 34 | +* 我们最终的动态库中不嵌入Python或者其他任何语言的解释器。模型配置解析,数据读取均交由其他语言完成 |
| 35 | + |
| 36 | +现阶段Paddle有一个问题是,Paddle内嵌的Python解释器和外部使用的Python如果版本不同,会直接报错退出。 |
| 37 | + |
| 38 | +### Paddle动态库中,不引用其他动态库 |
| 39 | + |
| 40 | +* 即这个动态库是不依赖于其他任何文件的,可以在任何机器上执行的。 |
| 41 | + |
| 42 | +### 这个动态库使用C99标准的头文件导出一些函数,不使用/导出C++符号 |
| 43 | + |
| 44 | +* 由于C++编译器没有[名字修饰](https://en.wikipedia.org/wiki/Name_mangling#C.2B.2B)的规范,不同版本的编译器之间,对于同一段C++代码生成的符号可能不一致。而多语言接口需要直接读取生成的二进制(动态库),需要有稳定的导出符号。 |
| 45 | +* C语言是有导出符号的标准的,并且在常见的平台上,都是ABI调用标准的。 |
| 46 | +* 大多数语言都支持使用C语言API |
| 47 | + |
| 48 | +### 不导出Paddle内部的结构体、类,仅仅使用`void*`指针作为类型的句柄(handler) |
| 49 | + |
| 50 | +* Paddle内部的类为C++书写,直接导出到C的接口比较困难。 |
| 51 | +* 在C-API中使用`void*`来表示Paddle内部类。再在每一个API中自己检查类型。 |
| 52 | + |
| 53 | +```C |
| 54 | +// in matrix.cpp |
| 55 | +struct PaddleMatrix { |
| 56 | + int type; |
| 57 | + paddle::MatrixPtr mat; |
| 58 | +}; |
| 59 | + |
| 60 | +// in Paddle.h |
| 61 | +typedef void* PD_Matrix; |
| 62 | +extern "C" PD_Error getShape(PD_Matrix, uint64_t* height, uint64_t* width); |
| 63 | +``` |
| 64 | +
|
| 65 | +### 不使用SWIG这种代码生成器,而是手写多语言绑定 |
| 66 | +
|
| 67 | +* [SWIG](http://www.swig.org/)是一个多语言接口的代码生成器。他的目标是使用C/C++写代码,SWIG直接读取C/C++的头文件,生成各种语言的绑定代码。 |
| 68 | + * 对于多语言接口,SWIG需要写一个interface文件。这个文件具有独特的语法,学习成本高。且增加一个第三方语言,就需要对这个第三方语言增加一些定义。有的时候,interface文件的写法非常[tricky](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/api/Paddle.swig#L36)。社区贡献代码学习成本高。 |
| 69 | + * SWIG暴露的接口保留了C++的接口样式,很难保证多语言代码风格的一致性。(函数命名,错误处理) |
| 70 | + * 对于大多数语言,直接使用C语言的.h并不困难。例如Python的[cffi](https://cffi.readthedocs.io/en/latest/overview.html#simple-example-abi-level-in-line)或者[Cython](http://cython.org/), golang的[cgo](https://golang.org/cmd/cgo/)。 |
| 71 | + * swig支持的语言或者解释器有局限。例如对于Python,使用SWIG只支持CPython解释器,而不支持PyPy解释器。 |
| 72 | +
|
| 73 | +
|
| 74 | +## 原因列表 |
| 75 | +
|
| 76 | +| 结论 | 对比 | 原因 | |
| 77 | +|---| --- | --- | |
| 78 | +| 使用动态库 | 不实用静态库 | 解释型语言只能调用动态库,Paddle静态库链接复杂 | |
| 79 | +| 不嵌入其他语言解释器 | 不嵌入Python解释器 | Paddle C++目前嵌入Python解释器,会导致不同版本Python在一个进程里的bug | |
| 80 | +| 不引用其他动态库 | | Paddle一个动态库可以在任何Linux系统上运行 | |
| 81 | +| 使用C99做接口 | 不使用C++做接口 | C有标准的ABI,C99是目前C最广泛的使用标准(而不是C11,和C89) | |
| 82 | +| 使用void*作为类句柄 | 不显示的写每个类具体包含什么| 实现简单,并且让接口脱离实现细节 | |
| 83 | +| 手写多语言绑定 | 不使用SWIG | 写SWIG很tricky,社区参与难。SWIG生成的代码不能保证多语言代码风格的一致性 | |
| 84 | +
|
| 85 | +
|
| 86 | +## 简单实现 |
| 87 | +
|
| 88 | +TBD |
0 commit comments