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

中国软件开源创新大赛:飞桨框架任务挑战赛 赛题六 RFC #529

Merged
merged 13 commits into from
May 16, 2023
279 changes: 279 additions & 0 deletions rfcs/Docs/飞桨框架 C++ 文档抽取与展示.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
# 飞桨框架 C++ 文档抽取与展示

|领域 | 飞桨框架 C++ 文档抽取与展示 |
|---|--------------------------------|
|提交作者<input type="checkbox" class="rowselector hidden"> | Liyulingyue、gouzil |
|提交时间<input type="checkbox" class="rowselector hidden"> | 2023-04-27 |
|版本号 | V1.0 |
|依赖飞桨版本<input type="checkbox" class="rowselector hidden"> | paddlepaddle>2.4 |
|文件名 | 飞桨框架 C++ 文档抽取与展示.md<br> |


# 一、概述
## 1、相关背景

自 paddle 2.3 版本开始,飞桨深度学习框架提供定义与用法与相应 Python API 类似的 C++ API,其 API 命名、参数顺序及类型均和相应的 paddle Python API 对齐,可以通过查找相应 Python API 的官方文档了解其用法,并在自定义算子开发时使用。通过调用这些接口,可以省去封装基础运算的时间,从而提高开发效率。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

现在的 C++ API 主要在自定义算子场景下使用。
未来框架是有可能打造自己的 C++ 体系的,从这个角度出发:本赛题想做的范围其实更“广”,可以算作 C++ 体系构建的预备工作。


[中国软件开源创新大赛:飞桨框架任务挑战赛 赛题6](https://github.com/PaddlePaddle/Paddle/issues/53172#paddlepaddle06)要求为飞桨框架自动抽取和展示 C++ 文档,并上线至飞桨官网。

## 2、功能目标

在[飞桨API文档页面](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/api/index_cn.html)引入新的章节`PADDLE_API`,用于展示飞桨当前暴露给用户的C++ 接口。

展示的内容为全部被`PADDLE_API`修饰的成员,包括但不仅包括API、Class、宏定义。

不失一般的,对于所有的展示的内容,应包含namespace、定义、 接口注释等。特别的,对于不同的被`PADDLE_API`修饰的成员,需要展示不同的信息。以class为例,不仅需要展示类定义,还需要展示对应的成员函数(如果有)、成员变量(如果有);对于类Python 的 API,展示内容应与Python API文档对其,包括对应的Python API名称、API介绍、参数、返回值、示例代码。
Liyulingyue marked this conversation as resolved.
Show resolved Hide resolved

## 3、意义

提升c++开发用户的开发体验。

# 二、飞桨现状

## 1、文档生成与更新
飞桨当前的英文文档信息保存在源代码的注释中,中文文档在`paddle/docs`目录下。每天,后台拉取develop分支,抽取英文文档和中文文档,转换为html后展示在官网上。

其中,英文文档的抽取代码可以开源。

## 2、 C++ API
飞桨的 C++ API 体系还在建设中,最终暴露给用户的API信息通过在安装根目录`site-packages/paddle/include/paddle`中搜索`PADDLE_API`获取。当前有11个class,450个API以及2个宏定义。其中6个class、3个API是具有注释说明的。

相比于Python API,无法在C++ API的源码中获取对应的API说明和示例代码。

# 三、设计思路与实现方案

## 1、 总述
综合考虑对当前的框架体系,拟通过人工构造与自动化脚本相结合的方式构造C++ 文档。

其中,能够通过自动化脚本获取的信息有:
- 每个文件或namespace包含的API名称、Class名称、宏定义等信息。可以通过遍历文件的方式构造能够在在主页上展示的`Overview`。
- 每个API、Class等对应的文件路径、接口注释、命名空间、返回值信息。
- 类Python 的 C++ API对应的Python API信息。由于两种语言的API命名几乎保持一致,可以通过搜索匹配的方式获取对应的Python API名称、说明等信息。

无法确定能够通过自动化脚本获取的信息有:
- C++ API的参数信息和返回值信息,如果C++ API的参数信息完全与Python对齐,则C++文档直接抽取Python文档的参数信息即可,否则,可能需要人工补足。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

对于 C++ API的参数信息和返回值信息这个表述,是可以自动化获取的吧?因为在解析 API 函数签名的时候,已经可以解析出参数类型和返回值类型了。
比如 PADDLE_API Tensor abs(const Tensor& x); 解析函数签名的时候,可以自动解析出输入 x 类型是 const Tensor&,返回值类型是 Tensor

此处表达的是想展示这样的信息吗? **x** (Tensor) - 输入的 Tensor,数据类型为:float32、float64。
想要展示对输入和输出参数的具体描述,这个信息如果要手工补足,有手动维护成本的话,不展示即可~~

- C++ API的示例代码。

考虑到赛题需求以及整个体系的维护性,仅当`类 Python 的 C++ API`的参数信息能够与python文档完全对应时,拷贝python文档的参数解释信息。

更进一步地,上述抽取和补足工作的成果应由两部份组成。
- 总览,提供一个用于快速搜索的界面。例如,以 API、Class、Enum等定义类型为一级标题,namespace为二级标题的导航界面。
- 单独介绍,对于每个API、Class、Enum都提供一个单独的介绍页面。

此外,可以进一步在docs下构造C++的示例代码运行测试脚本,以提高文档质量。

## 2、 C++ API抽取

C++ API抽取可以通过Python脚本解析`site-packages/paddle/include/paddle`文件实现。

## 3、 C++ API 与 Python API 对齐

仅类Python 的 C++ API 需要与 Python API 信息对齐。对于这部分API,首先根据C++ API文件名直接匹配对应的Python API,再对这部分信息进行核验,生成文档映射表。
Comment on lines +71 to +73
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C++ 和 Python 对齐这块,参考上一次 review 的 comment,仍有疑问需要解答:

  1. 由于一些历史原因,部分 C++ API 仍未和 Python API 完全对齐,这些 C++ API 如何与 Python 对齐?
  2. 如果是手工对齐的话,如果 C++ API 的参数和用法未来发生变化,是否还要对文档做手工维护呢?

需要想个办法避免 API 对齐的手动维护成本~~否则后续开发时,非常容易造成 C++ 和 Python 不一致的情况。

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

可以参考 paddle/phi/api/ext/tensor_compat.h 文件,这个文件里维护了 C++ 和 Python API 完全对齐的 API 列表。就不需要再核验 C++ 和 Python API 是否匹配啦~

PS-这个文件的作用:tensor_compat.h 文件里的 API 是经过我们对齐后的、稳定的 API,因此外部用户使用的时候,可以直接用 paddle::abs 的方式来调用。对于不稳定的 API,或者说 api.h 头文件里的 API,外部用户调用的时候,得加上 experimental 命名空间,例如 arange 这个 API 没放到 tensor_compat.h 文件里,用户调用的时候,就得用 paddle::experimental::arange 的方式调用。


## 4、 OverView 页面
Overview 页面风格应与 [Python 的 Overview](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/api/index_cn.html)保持一致。

一个简易的示例页面如下:

```python
# C++ 文档
欢迎使用飞桨框架(PaddlePaddle),PaddlePaddle 是一个易用、高效、灵活、可扩展的深度学习框架,致力于让深度学习技术的创新与应用更简单。

在本版本中,飞桨框架对 C++ 接口做了许多优化,您可以参考下表来了解飞桨框架最新版的 C++ 目录结构与说明。更详细的说明,请参见 版本说明 。此外,您可参考 PaddlePaddle 的 GitHub 了解详情。

## name1.h
name1.h的介绍

### class
- class name 1
- class name 2
### API
- API name 1
- API name 2

## name2.h
name1.h的介绍

### class
- class name 1
- class name 2
### API
- API name 1
- API name 2

```

## 5、 C++ API文档
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

本章节仅展示了与 Python API 对齐的 C++ API 示例。对于 C++ 专属的 API,展示示例是什么样的?建议也提供一份示例代码~


C++ 文档能够自动更新,C++ 文档的历史存档以类似于Paddle Python中文文档的形式,存放在Docs目录下。

下面是一个 C++ API文档的示例:

```python
.. _cn_api_functionname:

functionname
-------------------------------

.. cpp:function::functionname(para1, para2, para3)
介绍文本

定义目录
:::::::::::::::::::::
path

返回值
:::::::::::::::::::::
介绍文本

```

## 6、 类 Python 的 C++ API文档

对于类 Python 的 C++ API,如果参数信息能够与Python完全对齐,则需要同步展示Python的信息,并且复用部分Python的说明文本。

`PADDLE_API Tensor abs(const Tensor& x);`是一个类 Python 的 C++ API,API 能够完全与 Python 端对齐,故展示页面不仅要展示C++的信息,还要展示对应的 Python API 信息。以abs为例,其中文文档内容应为:

```python
.. _cn_api_fluid_layers_abs:

abs
-------------------------------

.. cpp:function:: PADDLE_API Tensor paddle::experimental::abs(const Tensor& x)

绝对值函数。

.. math::
out = |x|

对应的Python API
:::::::::::::::::::::
[paddle.abs(x, name=None)](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/api/paddle/abs_cn.html)

定义目录
:::::::::::::::::::::
paddle\phi\api\include\api.h
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tips:此处的目录,是否还可以直接链接到 github 的代码位置?


参数
:::::::::::::::::::::
- **x** (Tensor) - 输入的 Tensor。

返回
:::::::::::::::::::::
输出 Tensor,与 ``x`` 维度相同。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

能够对齐的 C++ API,复用 python API 并展示 python 链接的方法很棒~~

那么对于 python 文档里面已有的信息,是不是就不需要重复 copy 到 C++ 文档里了?
以 abs 为例,参数返回 这两部分的内容,还有从 python 文档里 copy 过来的必要吗?

Comment on lines +178 to +184
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

个人理解,此处没必要把 python 的文档再 copy 过来,直接展示 python 文档的链接即可,基于两个理由:

  1. copy 过来会增加你们的开发成本:比如 Python API 会带有 name 参数,而 C++ 是没有的,copy 过来时还要对这部分逻辑做处理。这部分的开发成本是可以节省下来的,节省下来的人力,可以用来思考下其他的问题,比如 overview 是否有更好的展示方式?class 文档有没有更清晰的文档展示形态?
  2. 遵循“若无必要,勿增实体”的奥卡姆剃刀原则:从查看文档的用户来讲,他们很可能并不清楚,哪些 API 是对齐的,哪些是未对齐的,以及背后的历史背景。此时如果有些 API 提供了 “参数” 和 “返回值” 的中文解释,但有些 API 没有的话,用户很可能会有疑问,此时会增加用户的理解成本。感觉直接这样给用户提示就可以: 本 API 与 Python API 对齐,详细用法可参考链接:[paddle.abs(x, name=None)](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/api/paddle/abs_cn.html)


```

该API对应的Python文档为:

```python
.. _cn_api_fluid_layers_abs:

abs
-------------------------------

.. py:function:: paddle.abs(x, name=None)

绝对值函数。

.. math::
out = |x|

参数
:::::::::
- **x** (Tensor) - 输入的 Tensor,数据类型为:float32、float64。
- **name** (str,可选) - 具体用法请参见 :ref:`api_guide_Name`,一般无需设置,默认值为 None。

返回
:::::::::
输出 Tensor,与 ``x`` 维度相同、数据类型相同。

代码示例
:::::::::

COPY-FROM: paddle.abs

```

## 7、 C++ class文档

C++ class文档的示例模板如下:

```python

.. _cn_api_classname:

classname
-------------------------------

.. cpp:class:: classname(para1, para2, para3)
介绍文本

定义目录
:::::::::::::::::::::
path

参数
:::::::::::::::::::::
- **para1** (type) - 介绍文本。
- **para2** (type) - 介绍文本。
- **para3** (type) - 介绍文本。

方法
:::::::::::::::::::::

fun1
'''''''''
介绍文本

**参数**
- **para1** (type) - 介绍文本

**返回**
介绍文本

fun2
'''''''''
介绍文本

**参数**
- **para1** (type) - 介绍文本

**返回**
介绍文本
```

## 8、 日常更新与维护

每日更新时,拉取最新paddle源码,并编译对应的whl包,用于自动提取PADDLE_API。对于不同的提取结果,采用不同的策略:
- 对于已有但是没有抽取到的信息,可以被视为退场,docs目录下虽然保存对应的rst文件,但不在官网页面展示
Liyulingyue marked this conversation as resolved.
Show resolved Hide resolved
- 对于新增或修改的信息,通过脚本自动抽取对应信息生成rst文档。
- 对于类Python 的 C++ API,不仅需要解析C++的文件信息,还需要根据对应Python 文档修改rst内容。

## 9、 扩展与维护成本

综合考虑赛题要求、赛题导师和参赛成员的意见,当前的 rfc 方案侧重于0人工维护。当前的解决方案几乎可以实现0人工维护,但在下述情况下,仍需要进行人工维护:
1. 补充注释:随着C++ 算子的开发,注释必然日趋规范,目前的API注释比仅为 3/460,即当前的注释规范可能并不稳定。在后续的工作中,注释规范可能发生变化,当我们确定了注释的格式后,需要对文档抽取函数进行少量更改,以适配新的注释格式抽取API的说明。维护量:低
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

对齐下我们的理解:此处想做的是提供一种 “根据指定格式来自动解析注释” 的机制。

如果是这样的话,我觉得这个部分很有意义,有利于后续 PADDLE_API 的扩展和维护。并且鉴于现在只有很少量的 API 和 class 写了注释,修改这些注释代码的成本也不高。
如果后续注释格式确定了,可以在文档里补充下格式(比如说根据注释格式里的什么标志,确定解析该注释并展示在文档中)


此外,Paddle的文档应保持对用户友好,为了达成这一要求,仍需进行的工作,以及这些工作在后续API变更中带来的维护压力如下:
1. 补充说明注释:补充C++ 所有函数、类的注释。补充后,文档信息可以自动抽取与展示。工作量:大、维护压力:中。
2. 补充示例代码:补充C++ 所有函数、类的示例代码。补充后,文档信息可以自动抽取与展示。工作量:大、维护压力:中。
3. 补充中文信息:补充C++ 所有函数、类的中文信息,包括但不限于说明、参数解释、返回值表述。以目前的Python 中文文档为例,这些内容需要手动更改,无法自动地和代码内容对齐。工作量:大、维护压力:大。
4. 映射类 Python C++:对所有的类Python 的 C++ API进行映射,包括但不限于两者的区别和差异,这些内容需要手动更改,可能在某次更新后,C++ API彻底与Python API割裂,不具有对应关系。工作量:大、维护压力:大。

# 四、测试和验收的考量

C++文档上线官网的develop分支。

# 五、排期规划
整个任务的规划实时步骤如下
1. 构建PADDLE_API抽取脚本,实现PADDLE_API抽取。(基于CppHeaderParser已实现)
2. 构建OverView页面和rst页面。(部分实现)
3. 构建API对齐脚本,用于根据抽取的API匹配当前已有的Python文档,生成对应rst。
4. C++ 文档接入官网文档页面develop分支
5. C++ 文档自动化更新脚本接入官网文档页面develop分支

# 六、影响面

仅对文档展示页面存在影响。