Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C-API design survey #955

Closed
jacquesqiao opened this issue Dec 20, 2016 · 5 comments
Closed

C-API design survey #955

jacquesqiao opened this issue Dec 20, 2016 · 5 comments

Comments

@jacquesqiao
Copy link
Member

下面是对tensorflow和mxnet的api设计做了一些调研的结论。

共同点:

  • 1,都使用标准c对外提供api接口。
  • 2,所有对外接口封装到一个文件中,例:c_api.h。
  • 3,使用下面的类似结构进行调用状态的传递,封装的语言需要对结果进行check。
struct Status {
    const char* msg;
    int32_t code;
};
  • 4,资源需要有宿主语言来释放。

不同点:

  • 1,mxnet使用了exception,标准API通过try catch封装内部接口。TF不使用exception。
  • 2,mxnet使用python直接load so的方式进行封装。TF的官方python binding使用了swig。

经过一些讨论得到的结论(#849):

  • 1,所有API通过一个类似c_api.h方式对外暴露。
  • 2,错误信息使用Status的方式,宿主语言做错误检查。
  • 3,不使用exception。
  • 4,不使用swig,直接封装so的方式。
  • 5,资源需要由宿主语言来释放。

实现细节。

TF封装API的完整demo。

  • c_api.h中的接口申明。
extern TF_Session* TF_NewSession(TF_Graph* graph, const TF_SessionOptions* opts, TF_Status* status);
  • c_api.cc中的接口实现。
TF_Session* TF_NewSession(TF_Graph* graph, const TF_SessionOptions* opt,
                          TF_Status* status) {
  //初始化并做一些检查工作,根据检查结果设置status
  Session* session;
  status->status = NewSession(opt->options, &session);
  return new TF_Session(session, graph);
}
  • session.py中封装和session相关的api。通过try ... finally ... 方式进行资源释放。
class Session:
    def __init__():
      opts = tf_session.TF_NewSessionOptions(target=target, config=config)
      try:
        status = tf_session.TF_NewStatus()
        try:
          self._session = tf_session.TF_NewSession(opts, status)
          # 状态检查
          if tf_session.TF_GetCode(status) != 0:
            raise RuntimeError(compat.as_text(tf_session.TF_Message(status)))
        finally:
          # 释放资源
          tf_session.TF_DeleteStatus(status)
      finally:
        # 释放资源
        tf_session.TF_DeleteSessionOptions(opts)

mxnet封装demo

  • 所有接口封装在c_api.h中。
externC" int MXOptimizerFindCreator(const char *key, OptimizerCreator *out);
  • 接口实现在c_api.cc中。用API_BEGIN() 开始,用 API_END() 结束。实际上是使用了try catch。返回值是int,0代表正常,非0代表有异常。

  • 接口实现在 c_api.cc中。

int MXOptimizerCreateOptimizer(OptimizerCreator creator,
                               mx_uint num_param,
                               const char **keys,
                               const char **vals,
                               OptimizerHandle *out) {
  API_BEGIN();
  # user logic
  API_END();
}
  • python 调用方式
# 直接从so中load所有的接口。
_LIB = _load_lib()
# check_call检查返回值,
check_call(_LIB.MXOptimizerFindCreator(c_str(name), ctypes.byref(creator)))

def check_call(ret):
    """Check the return value of C API call

    This function will raise exception when error occurs.
    Wrap every API call with this function

    Parameters
    ----------
    ret : int
        return value from API calls
    """
    if ret != 0:
        raise MXNetError(py_str(_LIB.MXGetLastError()))
@wangkuiyi
Copy link
Collaborator

wangkuiyi commented Dec 20, 2016

@jacquesqiao 的比较结果。对我们的API设计启发很大呀!

有几个细节问题:

  1. MxNet 的 API_BEGINAPI_END 是不是两个macro deifnition,一个是 try {,一个是 catch (...) { ...} ,这样把C++里的exception在API实现里处理掉,不会反馈给宿主语言程序?

  2. MxNet 中Python程序调用C接口的方式有点儿太直接了是吧?比如参数需要用 c_str() 包一下,结果需要 check_all 检查一下。我们貌似应该对Python写一套Python库来提供Python风格的API,这样让用户调用我们的Python库,而我们的Python库来调用 check_callc_str

@jacquesqiao
Copy link
Member Author

@wangkuiyi
1,是的,定义如下:

#define API_BEGIN() try {

#define API_END() } catch(dmlc::Error &_except_) { return MXAPIHandleException(_except_); } return 0;

2,对的。对api的直接封装是官方手写的,相当于手写了swig封装的部分,对用户提供的api是封装之后的,所以用户无需关心这个部分。我们也是类似的方式,底层的c_api需要封装之后,对外提供python风格的接口。

@wangkuiyi
Copy link
Collaborator

wangkuiyi commented Dec 20, 2016

还有一个问题:struct Status 里可否只有一个 char* msg,而不需要 int code 呢?—— 用 msg == NULL表示没有错误。

我见到很多error的实现(包括Objective-C里的标准error实现)都是一个字符串加一个code,但是实际上两者的内容重复,而且很不容易分配code。

我注意到在Go语言里定义了一种特殊的类型error,其实就是一个字符串,并没有那个code。我感觉比较好使。

@wangkuiyi wangkuiyi assigned wangkuiyi, reyoung and gangliao and unassigned wangkuiyi Dec 20, 2016
@reyoung
Copy link
Collaborator

reyoung commented Dec 20, 2016

还有一个问题:struct Status 里可否只有一个 char* msg,而不需要 int code 呢?—— 用 msg == NULL表示没有错误。

使用const char* 返回错误挺好的。

@jacquesqiao
Copy link
Member Author

status这个部分我可以去了解一下,实际上对于mxnet,code只有两个,一个是0,代表正常,一个是-1,代表有异常。

@jacquesqiao jacquesqiao self-assigned this Dec 20, 2016
zhhsplendid pushed a commit to zhhsplendid/Paddle that referenced this issue Sep 25, 2019
* update paddle-mobile doc

* delete untracked file

* add install appendix

* add install appendix

* add install appendix

* add install appendix
wangxicoding pushed a commit to wangxicoding/Paddle that referenced this issue Dec 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
Development

No branches or pull requests

5 participants