Skip to content

Commit f979b12

Browse files
authored
Merge pull request #1062 from reyoung/feature/c_api
C-API for inference.
2 parents 9ff5f2a + 4e0f72e commit f979b12

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2365
-13
lines changed

CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ option(WITH_DOC "Compile PaddlePaddle with documentation" OFF)
5050
option(WITH_COVERAGE "Compile PaddlePaddle with code coverage" OFF)
5151
option(COVERALLS_UPLOAD "Package code coverage data to coveralls" OFF)
5252
option(ON_TRAVIS "Exclude special unit test on Travis CI" OFF)
53+
option(WITH_C_API "Compile PaddlePaddle with C-API(Prediction)" OFF)
5354

5455
# CMAKE_BUILD_TYPE
5556
if(NOT CMAKE_BUILD_TYPE)
@@ -75,6 +76,13 @@ endif(ANDROID)
7576

7677
set(THIRD_PARTY_PATH "${PROJ_ROOT}/third_party" CACHE STRING
7778
"A path setting third party libraries download & build directories.")
79+
80+
if (WITH_C_API AND WITH_PYTHON)
81+
message(WARNING "It is suggest not embedded a python interpreter in Paddle "
82+
"when using C-API. It will give an unpredictable behavior when using a "
83+
"different Python interpreter from compiling.")
84+
endif()
85+
7886
########################################################################################
7987

8088
include(external/zlib) # download, build, install zlib

cmake/flags.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,4 @@ if(CUDA_ARCH)
197197
endif()
198198

199199
set(CUDA_NVCC_FLAGS ${__arch_flags} ${CUDA_NVCC_FLAGS})
200+

doc/design/multi_language_interface/why_plain_c.md renamed to doc/design/multi_language_interface/00.why_plain_c.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,32 +58,32 @@ typedef void* paddle_matrix;
5858
typedef int paddle_error;
5959

6060
extern "C"
61-
paddle_error paddle_matrix_shape(paddle_matrix matrix,
62-
uint64_t* width,
63-
uint64_t* height);
61+
paddle_error paddle_matrix_get_shape(paddle_matrix matrix,
62+
uint64_t* width,
63+
uint64_t* height);
6464
```
6565
而在CPP里面实现这个C的接口,文件 `paddle_matrix.cpp`
6666
6767
```cpp
68-
#include "paddle/math/matrix.hpp"
68+
#include "paddle/math/matrix.h"
6969
extern "C"
7070
paddle_error paddle_matrix_shape(paddle_matrix matrix,
7171
uint64_t *width,
7272
uint64_t *height) {
73-
auto m = (paddle::math::matrix*)(matrix);
73+
auto m = (paddle::capi::CMatrix*)(matrix);
7474
*width = m->width();
7575
*height = m->height();
7676
}
7777
```
7878

79-
其中`paddle/math/matrix.hpp`文件内容为:
79+
其中`paddle/capi/CMatrix.hpp`文件内容为:
8080

8181
```cpp
8282
namespace paddle {
8383
namespace math {
8484

85-
class Matrix {
86-
//...
85+
class CMatrix {
86+
std::shared_ptr<paddle::Matrix> mat;
8787
};
8888

8989
} // namespace math
@@ -113,6 +113,6 @@ class Matrix {
113113
| 手写多语言绑定 | 不使用SWIG | 使用SWIG需要多语言绑定的开发人员熟练掌握SWIG配置,社区参与困难。SWIG生成的代码不能保证多语言代码风格的一致性 |
114114
115115
116-
## 简单实现
116+
## 实现
117117
118-
TBD
118+
参考[Inference implementation](01.inference_implementation.md)
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# C-API 模型推断实现文档
2+
3+
本文档描述Paddle C-API的实现细节。Paddle C-API是多语言API的基础部分。Paddle需要暴露的API很多。先实现模型推断的API,通过模型推断API的实现作为一个样例,来进行讨论。至于为什么需要C-API,请参考[Why Plain C](./00.why_plain_c.md)
4+
5+
## Table of Contents
6+
* [C-API 模型推断实现文档](#c-api-模型推断实现文档)
7+
* [暴露接口原则](#暴露接口原则)
8+
* [目录结构](#目录结构)
9+
* [实现方式](#实现方式)
10+
* [capi.h](#capih)
11+
* [具体某种类型的头文件](#具体某种类型的头文件)
12+
* [capi_private.h](#capi_privateh)
13+
* [具体某种类型的实现文件](#具体某种类型的实现文件)
14+
* [libpaddle_capi_shared.{so, dylib}](#libpaddle_capi_sharedso-dylib)
15+
* [libpaddle_capi_whole.a](#libpaddle_capi_wholea)
16+
* [examples](#examples)
17+
* [编译选项](#编译选项)
18+
19+
20+
## 暴露接口原则
21+
22+
1. 所有的接口均为C接口。即使用`extern "C"`
23+
2. 除构造某种类型的函数(`paddle_matrix_create`等),其他函数均返回`paddle_error`。且调用时不能抛出异常或出现运行时错误。
24+
3. 所有类型名为`paddle_类型名`,所有与类型相关的函数,函数名为`paddle_类型名_函数名`
25+
4. 如果某一个Paddle Core概念(GradientMachine/Matrix)需要被暴露到其他语言,那么
26+
* 为了暴露的接口尽量简单。只暴露概念的接口,而不暴露概念的实现。即暴露`GradientMachine`或者`Matrix`但不暴露`RecurrentGradientMachine`和`CpuSparseMatrix`。
27+
* 暴露这个概念必要函数。`必要`是指,即完成某一个任务的最少函数。
28+
5. 不在`capi`接口层做过多封装。
29+
* 如果某一个Paddle概念必须要暴露,但是又过于琐碎。不在`capi`这一层进行封装,而是直接修改Paddle Core。让Paddle核心中,这一概念不再琐碎。
30+
31+
32+
## 目录结构
33+
34+
```text
35+
Paddle
36+
`-- paddle
37+
`-- capi
38+
`-- examples # The example project for C-API.
39+
`-- tests # unittests for C-API
40+
`-- capi.h # C-API header file.
41+
`-- capi_private.h # The shared header file between implementation sources.
42+
`-- matrix.{h, cpp}
43+
`-- gradient_machine.{h, cpp}
44+
`-- ...
45+
```
46+
47+
48+
Paddle的C-API目录结构如上图表所示。这个目录中除了`capi_private.h`之外的所有头文件,均会被安装到include/paddle路径下。C-API生成的二进制文件会被安装到`lib`目录下。即,安装后的目录结构为
49+
50+
```text
51+
`-- include
52+
`-- paddle
53+
`-- capi.h
54+
`-- matrix.h
55+
`-- gradient_machine.h
56+
`-- ...
57+
`-- lib
58+
`-- libpaddle_capi_shared.{so, dylib} # In mac, dynamic libary's file name extention is `dylib`
59+
`-- libpaddle_capi_whole.a # static library for all symbols of Paddle.
60+
```
61+
62+
## 实现方式
63+
64+
下面分别介绍某一类文件的实现方式。
65+
66+
### capi.h
67+
68+
`capi.h`是用户使用C-API时所唯一需要引入的头文件。在`capi.h`中,引入了类型的头文件,`matrix.h`, `gradient_machine.h`。在引入其他类型的头文件时,使用相对路径的引用方式。即`#include "matrix.h"`
69+
70+
### 具体某种类型的头文件
71+
72+
具体某种类型的头文件,即例如`matrix.h``gradient_machine.h`等。在这些头文件中,包含了某种类型的类型定义和暴露的全部函数。
73+
74+
这个头文件不假设其他文件的引用顺序,即使用户直接引用某种类型的头文件,也不应该报错(虽然不鼓励这样)。如果某一个类型需要引用另一个类型,例如`gradient_machine`需要引用`matrix`,则直接引入另一种类型的头文件,即`#include "matrix.h"`
75+
76+
### capi_private.h
77+
78+
`capi_prviate.h`是各个实现中共享的头文件,他主要包含了实际暴露的类型结构。在用户使用C-API时,Paddle的类型全部退化成`void *`,即`typedef paddle_matrix void*`。但,对于每种C-API暴露的类型,均是在`capi_private.h`中实现的结构体。
79+
80+
```cpp
81+
struct CMatrix {
82+
int type = MatrixType;
83+
std::shared_ptr<paddle::Matrix> mat;
84+
};
85+
```
86+
87+
通常,这个结构体包含两个项目。
88+
89+
* `type`是一个类型的标志。对于每种类型,type字段均不尽相同。这样,即使C-API接受的类型全是`void *`,我们也可以确定每一个参数的类型。
90+
91+
```cpp
92+
void some_c_api_function(void* some_instance) {
93+
int* type = (int *) some_instance;
94+
switch (*type) {
95+
case MatrixType:
96+
CMatrix* mat = (CMatrix *) some_instance;
97+
...
98+
...
99+
}
100+
}
101+
```
102+
* 这个结构体中的另一个项目是,Paddle Core中这一类型接口的智能指针(shared_ptr)。
103+
* 使用智能指针的原因是: 用户可以安全的释放某个C-API的实例,而不必在意Paddle Core是否还在使用这个实例。
104+
* 例如,用户通过C-API获得了神经网络的参数实例。当用户使用完这个参数后,直接删除这个参数即可。即便Paddle Core中的模型还在使用这个参数,这个参数也不会一并删除。
105+
106+
### 具体某种类型的实现文件
107+
108+
具体某种类型的实现文件,即`matrix.cpp`, `gradient_machine.cpp`等文件。在这些文件中,使用C++ 11实现了C-API的接口,并且使用`extern "C"`导出这些接口。在实现过程中,对输入参数的安全性进行了必要的判断,并将C-API接口的参数转发给`Paddle Core`
109+
110+
### libpaddle\_capi_shared.{so, dylib}
111+
112+
`libpaddle_capi_shared`是C-API导出的动态库。这个动态库的连接参数与Paddle的其他二进制(例如`paddle_trainer`)类似。用户可以直接使用这个动态库来引入Paddle C-API。具体使用方法为`-lpaddle_capi_shared`。
113+
114+
### libpaddle\_capi_whole.a
115+
116+
`libpaddle_capi_whole`是C-API导出的静态库。这个静态库包含了Paddle的全部符号。他是将`libpaddle_gserver.a`, `libpaddle_math.a`, `libpaddle_capi.a`等全部静态库中的目标文件全部打包后产生的文件。具体使用方法为`--whole-archive -lpaddle_capi_whole --no-whole-archive`。
117+
118+
119+
### examples
120+
121+
在样例中,使用`C99`开发了模型预测的样例代码。具体请参考[example/README.md](../../../paddle/capi/examples/README.md)。
122+
123+
## 编译选项
124+
125+
C-API的编译选项默认关闭,打开这个编译选项,需要在cmake的时候,设置
126+
127+
```bash
128+
cmake ${YOUR_SOURCE_ROOT} -DWITH_C_API=ON -DWITH_PYTHON=OFF -DWITH_SWIG_PY=OFF
129+
```
130+
131+
编译C-API的时候推荐Paddle不嵌入Python解释器,也不生成`SWIG`接口,具体原因参考[Why Plain C](./00.why_plain_c.md)。

paddle/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ add_subdirectory(pserver)
99
add_subdirectory(trainer)
1010
add_subdirectory(scripts)
1111

12+
if(WITH_C_API)
13+
add_subdirectory(capi)
14+
endif()
15+
1216
if(WITH_SWIG_PY)
1317
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in
1418
${CMAKE_CURRENT_SOURCE_DIR}/setup.py)

paddle/capi/Arguments.cpp

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License. */
14+
15+
#include "arguments.h"
16+
#include "capi_private.h"
17+
18+
using paddle::capi::cast;
19+
20+
#define castArg(v) cast<paddle::capi::CArguments>(v)
21+
#define castIVec(v) cast<paddle::capi::CIVector>(v)
22+
23+
extern "C" {
24+
paddle_arguments paddle_arguments_create_none() {
25+
return new paddle::capi::CArguments();
26+
}
27+
28+
paddle_error paddle_arguments_destroy(paddle_arguments args) {
29+
if (args == nullptr) return kPD_NULLPTR;
30+
delete castArg(args);
31+
return kPD_NO_ERROR;
32+
}
33+
34+
paddle_error paddle_arguments_get_size(paddle_arguments args, uint64_t* size) {
35+
if (args == nullptr || size == nullptr) return kPD_NULLPTR;
36+
*size = castArg(args)->args.size();
37+
return kPD_NO_ERROR;
38+
}
39+
40+
paddle_error paddle_arguments_resize(paddle_arguments args, uint64_t size) {
41+
if (args == nullptr) return kPD_NULLPTR;
42+
castArg(args)->args.resize(size);
43+
return kPD_NO_ERROR;
44+
}
45+
46+
paddle_error paddle_arguments_set_value(paddle_arguments args,
47+
uint64_t ID,
48+
paddle_matrix mat) {
49+
if (args == nullptr || mat == nullptr) return kPD_NULLPTR;
50+
auto m = paddle::capi::cast<paddle::capi::CMatrix>(mat);
51+
if (m->mat == nullptr) return kPD_NULLPTR;
52+
auto a = castArg(args);
53+
if (ID >= a->args.size()) return kPD_OUT_OF_RANGE;
54+
a->args[ID].value = m->mat;
55+
return kPD_NO_ERROR;
56+
}
57+
58+
paddle_error paddle_arguments_get_value(paddle_arguments args,
59+
uint64_t ID,
60+
paddle_matrix mat) {
61+
if (args == nullptr || mat == nullptr) return kPD_NULLPTR;
62+
auto m = paddle::capi::cast<paddle::capi::CMatrix>(mat);
63+
auto a = castArg(args);
64+
if (ID >= a->args.size()) return kPD_OUT_OF_RANGE;
65+
m->mat = a->args[ID].value;
66+
return kPD_NO_ERROR;
67+
}
68+
69+
paddle_error paddle_arguments_get_ids(paddle_arguments args,
70+
uint64_t ID,
71+
paddle_ivector ids) {
72+
if (args == nullptr || ids == nullptr) return kPD_NULLPTR;
73+
auto iv = castIVec(ids);
74+
auto a = castArg(args);
75+
if (ID >= a->args.size()) return kPD_OUT_OF_RANGE;
76+
iv->vec = a->args[ID].ids;
77+
return kPD_NO_ERROR;
78+
}
79+
80+
paddle_error paddle_arguments_set_ids(paddle_arguments args,
81+
uint64_t ID,
82+
paddle_ivector ids) {
83+
//! TODO(lizhao): Complete this method.
84+
if (args == nullptr || ids == nullptr) return kPD_NULLPTR;
85+
auto iv = paddle::capi::cast<paddle::capi::CIVector>(ids);
86+
if (iv->vec == nullptr) return kPD_NULLPTR;
87+
auto a = castArg(args);
88+
if (ID >= a->args.size()) return kPD_OUT_OF_RANGE;
89+
a->args[ID].ids = iv->vec;
90+
return kPD_NO_ERROR;
91+
}
92+
93+
paddle_error paddle_arguments_set_sequence_start_pos(paddle_arguments args,
94+
uint64_t ID,
95+
uint32_t nestedLevel,
96+
paddle_ivector seqPos) {
97+
if (args == nullptr || seqPos == nullptr) return kPD_NULLPTR;
98+
auto iv = paddle::capi::cast<paddle::capi::CIVector>(seqPos);
99+
if (iv->vec == nullptr) return kPD_NULLPTR;
100+
auto a = castArg(args);
101+
return a->accessSeqPos(ID, nestedLevel, [&iv](paddle::ICpuGpuVectorPtr& ptr) {
102+
ptr = std::make_shared<paddle::ICpuGpuVector>(iv->vec);
103+
});
104+
}
105+
106+
paddle_error paddle_arguments_get_sequence_start_pos(paddle_arguments args,
107+
uint64_t ID,
108+
uint32_t nestedLevel,
109+
paddle_ivector seqPos) {
110+
if (args == nullptr || seqPos == nullptr) return kPD_NULLPTR;
111+
auto iv = paddle::capi::cast<paddle::capi::CIVector>(seqPos);
112+
auto a = castArg(args);
113+
return a->accessSeqPos(ID, nestedLevel, [&iv](paddle::ICpuGpuVectorPtr& ptr) {
114+
iv->vec = ptr->getMutableVector(false);
115+
});
116+
}
117+
}

0 commit comments

Comments
 (0)