- 数据分析
- 数据工程
- 机器学习
【免费下载链接】cudf
cuDF - GPU DataFrame Library
cuDF 是 NVIDIA 开源的 GPU 加速 DataFrame 库,其 C++ 核心引擎 libcudf 的公开 API 文档全部由源码中的 Doxygen 注释自动生成。本文以 cuDF 仓库中 libcudf 的官方文档指南(cpp/doxygen/developer_guide/DOCUMENTATION.md,对应 Sphinx 入口 docs/cudf/source/libcudf/developer_guide/DOCUMENTATION.rst)为主体,结合仓库内真实的 Doxyfile、分组定义头文件 doxygen_groups.h 与若干公开头文件源码,系统讲解:如何为 libcudf 的 C++/CUDA 源码书写规范的 Doxygen 块注释、如何使用分组(Group/Module)组织 API、如何配置并构建 HTML 文档,以及 CI 中如何校验文档告警。读完本文,你将能直接为 cuDF 仓库新增或修改 API 时写出风格统一、可被 Doxygen 正确渲染并被搜索引擎与文档工具索引的注释。
一、为什么需要一套统一的文档指南
libcudf 是一个体量庞大且类型体系复杂的 C++ 库:它的公开头文件数量以百计,类型包括数值、时间戳、时长、定点数、字符串、字典、列表、结构体等,几乎所有算法都要面向这些类型做分派。当 API 数量与注释风格失控时,文档的可读性、可检索性与可维护性都会急剧恶化。
因此 libcudf 对所有 C++ 源文件统一采用 Doxygen 风格的注释格式,但只有公开 API 与公开类会被真正发布到 API 文档页面。这意味着:
- 公开头文件(
cpp/include/cudf/*.hpp)中的每个对外符号都应写完整注释; - 内部实现(
detail命名空间、src目录、测试代码)的注释可以更自由,但仍要遵循同一套书写习惯,方便源码阅读与代码审查。
该指南适用于仓库内所有.hpp、.cpp、.cu、.cuh等 C++ 相关文件。
二、版权许可头:每个文件的"门面"
每个 C++ 源文件的开头都应包含如下许可证头注释:
/* * SPDX-FileCopyrightText: Copyright (c) 2021-2022, NVIDIA CORPORATION. * SPDX-License-Identifier: Apache-2.0 */两个关键细节:
- 注释必须以
/*开头而不是/**。因为/**会被 Doxygen 当作文档块处理,而许可证头不应该出现在生成的文档中。 - 版权年份规则:
- 新建文件写创建年份,例如
2026; - 修改过的文件应写成区间,例如
2019-2026(创建年-修改年); - 如果只是纯格式调整(reformatting)而没有内容变化,可以不更新年份。
- 新建文件写创建年份,例如
在仓库中,几乎所有源文件都遵守这一约定,例如 cpp/include/cudf/filling.hpp 写的是Copyright (c) 2019-2026,而分组定义文件 cpp/include/doxygen_groups.h 写的是Copyright (c) 2021-2026——年份区间如实反映了文件的生命周期。
三、Doxygen 工具与 Doxyfile 关键配置
Doxygen 是一个从 C++ 注释生成 HTML 文档的工具,它识别块注释中的**近 200 个命令(tag)**并做专门的排版输出。libcudf 的文档生成行为全部由 cpp/doxygen/Doxyfile(当前版本 1.18.0)控制。
以下是该 Doxyfile 中对 libcudf 定制过的核心选项:
| 选项 | 取值 | 作用 |
|---|---|---|
PROJECT_NAME | libcudf | 主页面标题 |
PROJECT_NUMBER | $(RAPIDS_VERSION) | 版本号(由构建期注入,读取仓库根目录 VERSION) |
EXTENSION_MAPPING | cu=C++ cuh=C++ | 让 Doxygen 把.cu与.cuh(CUDA 源文件/头文件)按 C++ 解析 |
INPUT | main_page.md regex.md unicode.md developer_guide/*.md ../include ... | 内嵌 Markdown 文件与要处理的源码目录 |
FILE_PATTERNS | *.cpp *.hpp *.h *.c *.cu *.cuh | 参与处理的文件扩展名 |
RECURSIVE | YES | 递归扫描cpp/include下的所有子目录 |
EXCLUDE_PATTERNS | */nvtx/* */detail/* */cudf_test/* | 排除 NVTX、detail内部实现与测试辅助头文件,确保只发布公开 API |
EXCLUDE_SYMBOLS | org::apache *_impl *Impl | 排除第三方符号与实现细节 |
WARN_NO_PARAMDOC | YES | 对"缺少参数/返回值文档"发出告警,帮助作者补全注释 |
MARKDOWN_SUPPORT | YES | 支持注释块中的 Markdown(链接、表格、列表等) |
LAYOUT_FILE | DoxygenLayout.xml | 使用 cpp/doxygen/DoxygenLayout.xml 自定义页面布局 |
其中INPUT不仅包含cpp/include源码目录,还包含了 5 个开发者指南 Markdown 文件(BENCHMARKING.md、DOCUMENTATION.md、DEVELOPER_GUIDE.md、PROFILING.md、TESTING.md),以及cudf_test的部分辅助头文件(如 column_wrapper.hpp、column_utilities.hpp 等),使测试工具类也有文档。
四、块注释(Block Comments)书写规范
描述函数、类、其他类型、分组与文件时,统一使用下面的块注释风格:
/** * description text and * doxygen tags go here */要点:
- Doxygen 块以
/**开始、以*/结束,首尾两行除这两个标记外不能有任何其他字符(不要加-----或*****装饰线); - 块必须紧贴在其所描述的源码行之前,可以适当缩进以与代码垂直对齐;
/**与*/之间的每一行都应"空格 + 星号"开头,正文(包括 tag 声明)在星号后空一格再写。
对比:许可证头用
/*,文档块用/**,逻辑注释(//)绝不用于代码逻辑说明——Doxygen 风格注释只服务于 API 文档。
五、标签命名与 Markdown
- 所有 Doxygen 命令统一用
@前缀(如@brief、@code),不使用反斜杠形式; - 注释块内支持 Markdown 的子集:链接、表格、列表等均可使用;
- 尽量避免直接写 HTML 标签。Doxygen 的 Markdown 对 HTML 的支持有限,混合使用容易在生成的网页中出现渲染问题;
- 需要注意
%与管道符|在 Markdown 表格内的可读性限制,必要时调整措辞。
六、完整示例:文件、类、函数、枚举怎么写
指南给出了一份覆盖绝大多数场景的示例,浓缩了 libcudf 文档风格的全部要点:
/** * @file source_file.cpp * @brief Description of source file contents * * Longer description of the source file contents. */ /** * @brief One line description of the class * * @ingroup optional_predefined_group_id * * Longer, more detailed description of the class. * * @tparam T Short description of each template parameter * @tparam U Short description of each template parameter */ template <typename T, typename U> class example_class { void get_my_int(); ///< Simple members can be documented like this void set_my_int( int value ); ///< Try to use descriptive member names /** * @brief Short, one line description of the member function * * A more detailed description of what this function does and what * its logic does. * * @code * example_class<int> inst; * inst.set_my_int(5); * int output = inst.complicated_function(1,dptr,fptr); * @endcode * * @param[in] first This parameter is an input parameter to the function * @param[in,out] second This parameter is used both as an input and output * @param[out] third This parameter is an output of the function * * @return The result of the complex function */ T complicated_function(int first, double* second, float* third) { // Do not use doxygen-style block comments // for code logic documentation. } private: int my_int; ///< An example private member variable }; /** * @brief Short, one line description of this free function * * @ingroup optional_predefined_group_id * * A detailed description must start after a blank line. * * @code * template<typename T> * struct myfunctor { * bool operator()(T input) { return input % 2 > 0; } * }; * free_function<myfunctor,int>(myfunctor{},12); * @endcode * * @throw cudf::logic_error if `input_argument` is negative or zero * * @tparam functor_type The type of the functor * @tparam input_type The datatype of the input argument * * @param[in] functor The functor to be called on the input argument * @param[in] input_argument The input argument passed into the functor * @return The result of calling the functor on the input argument */ template <class functor_type, typename input_type> bool free_function(functor_type functor, input_type input_argument) { CUDF_EXPECTS( input_argument > 0, "input_argument must be positive"); return functor(input_argument); } /** * @brief Short, one line description * * @ingroup optional_predefined_group_id * * Optional, longer description. */ enum class example_enum { first_enum, ///< Description of the first enum second_enum, ///< Description of the second enum third_enum ///< Description of the third enum };从该示例可以提炼出 libcudf 的文档风格骨架:
- 简单成员(getter/setter、枚举值)用行尾
///<三斜杠注释即可; - 复杂成员与自由函数用完整块注释,正文按
@brief → 详细描述 → 示例代码 → @throw/@tparam/@param/@return的顺序组织; - 枚举、模板、命名空间等所有声明类型都能用同一套结构描述。
七、描述(Descriptions)的撰写规范
注释中的描述文字应清楚说明输出如何从输入产生,并涵盖:
- 性能与边界(boundary)注意事项;
- 参数值的取值范围与默认值;
- 空值(null)如何被处理或产生;
- 尽量附带一个简短的内联示例。
7.1@brief:一句话简介
@brief的文本应是一句简短的描述,因为 Doxygen 在页面中给它的展示空间很有限;@brief行之后必须紧跟一个空注释行;- 通常
@brief相当于标题而非完整句子,因此不需要句号;只有确实是句子时才加句号。
/** * @brief Short description or title * * Long description. * */7.2@copydoc:避免重复文档
头文件中的声明应文档完整。当函数定义与声明文档相同、或某个detail函数仅比公开函数多一个stream参数时,用@copydoc复用文档,避免拷贝粘贴导致的双份维护:
/** * @copydoc complicated_function(int,double*,float*) * * Any extra documentation. */对带stream的detail版本:
/** * @copydoc cudf::segmented_count_set_bits(bitmask_type const*,std::vector<size_type> const&) * * @param[in] stream Optional CUDA stream on which to execute kernels */ std::vector<size_type> segmented_count_set_bits(bitmask_type const* bitmask, std::vector<size_type> const& indices, cuda::stream_ref stream = cudf::get_default_stream());注意:@copydoc必须写完整的函数签名(含可选参数),Doxygen 才能正确定位到被复制的声明。
7.3 参数相关标签的固定顺序
函数注释块中,以下几组标签应按如下顺序出现在注释块尾部:
| 命令 | 说明 |
|---|---|
@throw | 说明函数在何种条件下抛出异常 |
@tparam | 每个模板参数的说明 |
@param | 每个函数参数的说明 |
@return | 对返回对象/值的简短说明 |
@throw
为函数可能抛出的每一个异常各写一行@throw。只需覆盖函数自身抛出的异常;如果函数调用的其他函数会抛异常,不需要在这里重复记录。异常名不要加反引号,以便 Doxygen 正确生成引用链接:
* @throw cudf::logic_error if `input_argument` is negative or zero说明:写
@throws也合法,但 VS Code 等编辑器只对@throw做语法高亮,因此仓库统一用单数形式@throw。
@tparam
为函数声明的每个模板参数写一行@tparam,参数名必须与模板参数名完全一致;描述应说明该参数的要求(例如 functor 的输入类型与输出):
* @tparam functor_type The type of the functor * @tparam input_type The datatype of the input argument@param
为函数的每个参数写一行@param,参数名必须与函数签名一致;当参数的输入/输出角色从声明中看不出来时,追加[in]、[out]或[in,out]:
* @param[in] first This parameter is an input parameter to the function * @param[in,out] second This parameter is used both as an input and output * @param[out] third This parameter is an output of the function建议把三列文本(@param[in]、参数名、描述)垂直对齐,便于在源码编辑器中阅读。描述通常像标题一样简练,是句子时才需要句号。
@return
若函数返回对象或值,在注释块末尾写一行@return,简要描述返回内容,不要包含返回类型:
/** * ... * * @return A new column of type INT32 and no nulls */7.4 内联示例(Inline Examples)
用@code/@endcode成对包裹代码示例。Doxygen 默认按所在源文件语言做语法高亮,也支持指定语言(如.py):
* @code * auto result = cudf::make_column( ); * @endcode* @code{.py} * import cudf * s = cudf.Series([1,2,3]) * @endcode伪代码示例(@code{.pseudo})在某些场景下比真实代码更清晰:
* Sometimes pseudocode is clearer. * @code{.pseudo} * s = int column of [ 1, 2, null, 4 ] * r = fill( s, [1, 2], 0 ) * r is now [ 1, 0, 0, 4 ] * @endcode写示例时建议使用完全限定类名,这样 Doxygen 才能在示例中创建引用链接:
* @code * auto result1 = make_column( ); // reference link will not be created * auto result2 = cudf::make_column( ); // reference link will be created * @endcode其他注意事项:
- 虽然三个反引号(```)也能渲染示例,但在 VS Code 中不如
@code醒目; - 不要在声明注释中使用
@example标签——否则 Doxygen 会把整个源文件当作示例源码,并把文件单独发布到输出的Examples页面。
7.5 弃用标记(Deprecations)
对将在未来版本移除的 API,加一行@deprecated,并在注释中说明替代 API:
/** * ... * * @deprecated This function is deprecated. Use another new function instead. */这与仓库的 API 淘汰策略一致:libcudf 演进较快,会在可能的情况下用deprecated属性 + Doxygen@deprecated双重标记,并建议替代方案;引入弃用的 PR 应打上 "deprecation" 标签,破坏性变更的 PR 打 "breaking" 标签。
八、命名空间(Namespaces)文档
Doxygen 输出包含一个Namespaces页面,展示所有带注释块的命名空间。示例:
/** * @brief cuDF interfaces * * This is the top-level namespace which contains all cuDF functions and types. */ namespace CUDF_EXPORT cudf {规则:
- 每个唯一的命名空间声明只写一次描述注释。如果出现多处描述,Doxygen 会以任意顺序聚合显示,造成混乱;
- 引入新命名空间时,只给其中一个声明写描述块。
九、分组与模块(Groups/Modules):组织 API 的核心机制
把声明分组成"模块"能帮助用户在 Doxygen 页面中快速找到 API。虽然功能相近的函数通常已经按头文件逻辑组织,但 Doxygen不会自动按这种方式分组,需要显式声明。
分组命令可以跨头文件、源文件甚至命名空间聚合公共函数,且组内可以嵌套子组。libcudf 的全部分组层次都定义在 cpp/include/doxygen_groups.h 这一个头文件中:
- 该文件不需要被任何源文件 include,它只被 Doxygen 工具消费,用于生成Modules页面;
- 只应通过修改该文件来新增或更新分组;
- 现有分组经过精心设计与命名,新增分组时要谨慎、保持风格一致。
从 doxygen_groups.h 可以看到顶层结构的实际形态,例如:
/** * @defgroup default_stream Default Stream * @defgroup memory_resource Memory Resource Management * @defgroup cudf_classes Classes * @{ * @defgroup column_classes Column * @{ * @defgroup column_factories Factories * @defgroup column_stream Column stream * @defgroup strings_classes Strings * @defgroup dictionary_classes Dictionary * @defgroup timestamp_classes Timestamp * @defgroup lists_classes Lists * @defgroup structs_classes Structs * @} * @defgroup table_classes Table * @defgroup scalar_classes Scalar * @{ * @defgroup scalar_factories Factories * @} * @defgroup fixed_point_classes Fixed Point * @} */往下还能看到column_apis(Column and Table)、datetime_apis(DateTime)、strings_apis(Strings)、dictionary_apis、io_apis、json_apis、lists_apis、labeling_apis、nvtext_apis等一组并列的顶层分组,以及它们各自的子组(如copy_gather、transformation_fill、aggregation_groupby等),这些分组 ID 与 docs/cudf/source/libcudf/api_docs 下的*.rst页面一一对应。
9.1 新 API 如何加入分组
创建新 API 时,用@ingroup标签指定 doxygen_groups.h 中已有的分组 ID:
namespace CUDF_EXPORT cudf { /** * @brief ... * * @ingroup transformation_fill * * @param ... * @return ... */ std::unique_ptr<column> fill(table_view const& input,...); } // namespace cudf也可以使用@addtogroup+@{ ... @}成对结构,把文件内后续的注释块自动纳入分组,省去逐个写@ingroup的麻烦:
namespace CUDF_EXPORT cudf { /** * @addtogroup transformation_fill * @{ */ /** * @brief ... * * @param ... * @return ... */ std::unique_ptr<column> fill(table_view const& input,...); /** @} */ } // namespace cudf几个关键细节:
@addtogroup命令块之后必须保留一个空行,让 Doxygen 知道它不作用于后面的源码;- 如果
@addtogroup+@{ ... @}对中包含命名空间声明,Doxygen 不会把组应用到其内部条目,所以应像上面示例那样把这一对结构放在命名空间花括号之内; - 分组标签职责总结:
| 标签/命令 | 使用位置 |
|---|---|
@defgroup | 仅用于 doxygen_groups.h,需包含组的标题 |
@ingroup | 头文件中各声明语句的 Doxygen 注释块内 |
@addtogroup | 同一文件内、命名空间内有多个声明时替代@ingroup,不要指定组标题 |
@{ ... @} | 只与@addtogroup搭配使用 |
9.2 仓库实例:从源码看规范落地
在 cpp/include/cudf/copying.hpp 中可以看到上述规范的真实落地。该文件先通过@addtogroup column_copy @{ ... @}把整组复制类 API 纳入分组,随后每个声明都有完整的@brief、@ingroup、@param、@throw、@return注释。例如gather的声明:
/** * @brief Gathers the specified rows (including null values) of a set of columns. * * @ingroup copy_gather * * Gathers the rows of the source columns according to `gather_map` such that row "i" * in the resulting table's columns will contain row "gather_map[i]" from the source columns. * The number of rows in the result table will be equal to the number of elements in * `gather_map`. * * A negative value `i` in the `gather_map` is interpreted as `i+n`, where * `n` is the number of rows in the `source_table`. * * @throws std::invalid_argument if gather_map contains null values. * * @param source_table The input columns whose rows will be gathered * @param gather_map View into a non-nullable column of integral indices that maps the * rows in the source columns to rows in the destination columns. * @param bounds_policy Policy to apply to account for possible out-of-bounds indices * `DONT_CHECK` skips all bounds checking for gather map values. `NULLIFY` coerces rows that * corresponds to out-of-bounds indices in the gather map to be null elements. ... * @param stream CUDA stream used for device memory operations and kernel launches * @param mr Memory resources used for temporary allocations and the returned table * @return Result of the gather */ std::unique_ptr<table> gather(table_view const& source_table, column_view const& gather_map, out_of_bounds_policy bounds_policy = out_of_bounds_policy::DONT_CHECK, cuda::stream_ref stream = cudf::get_default_stream(), cudf::memory_resources mr = cudf::get_current_device_resource_ref());同样,cpp/include/cudf/filling.hpp 展示了@addtogroup transformation_fill的用法,以及@throw对异常条件的逐条记录(类型不匹配、非法范围、内存重分配需求等)——这正是"描述应覆盖边界与异常"原则的直接体现。文档中的参数类型(column_view const&、cuda::stream_ref、cudf::memory_resources)也印证了开发者指南中规定的输入输出风格:输入用视图、流与内存资源作为尾部参数、输出用std::unique_ptr。
十、构建 Doxygen 输出
10.1 安装 Doxygen
推荐两种安装方式:
conda install doxygen # 或 sudo apt install doxygen也可以从源码自行构建安装。注意仓库 CI 期望的 Doxygen 版本为1.18.0(见下文 CI 校验部分)。
10.2 生成 HTML 文档
在包含Doxyfile的 cpp/doxygen 目录下直接运行:
cd cpp/doxygen doxygen也可以通过 CMake 构建目标生成(从 CMake 构建目录,例如cpp/build):
cmake --build . --target docs_cudf构建过程会读取并处理 cpp/include 目录下所有符合条件的源文件,输出到cpp/doxygen/html/目录;直接用浏览器打开其中的index.html即可查看本地结果。
10.3 在远程服务器上查看文档
如果文档构建在远程服务器上,可以用 Python 起一个简易 HTTP 服务:
cd html && python -m http.server然后在本地浏览器访问<IP地址>:8000(把 IP 换成运行 HTTP 服务的机器地址)。
10.4 输出范围说明
Doxygen 输出只面向公开 API 与公开类:detail目录与src下的实现文件通过EXCLUDE_PATTERNS被排除,不会进入发布文档。当构建/CI 系统发布时,生成的文档会成为 cuDF 官方文档中 libcudf 部分的内容(本仓库内对应 Sphinx 入口见 docs/cudf/source/libcudf/index.rst 与 api_docs 目录)。
十一、CI 中的文档校验
仓库的 CI 脚本 ci/checks/doxygen.sh 专门用于校验 Doxygen 告警,其逻辑值得每位贡献者了解:
- 版本检查:若系统中未安装 doxygen 则跳过(打印 warning 并正常退出 0);若版本不是 1.18.0,打印 "Unsupported doxygen version" 并跳过;
- 版本号注入:从仓库根目录 VERSION 文件解析出
RAPIDS_VERSION与RAPIDS_VERSION_MAJOR_MINOR并导出,供 Doxyfile 中的$(RAPIDS_VERSION)使用; - 告警捕获:在
cpp/doxygen目录下以cat Doxyfile ; echo QUIET = YES; echo GENERATE_HTML = NO的方式把配置与覆盖项管道给doxygen -(从标准输入读取配置),从而只做解析、不生成 HTML,快速暴露注释中的问题;同时过滤掉缺失 tag 文件类的已知噪音错误; - 结果判定:若 doxygen 返回非零或 stderr 非空,则以退出码 1 使 CI 失败。
这意味着:任何新增或修改的注释只要出现文档错误(如参数名不匹配、缺少参数文档),都会在 CI 中被拦截。因此写注释时务必保证@param/@tparam名称与声明完全一致,并覆盖全部参数与返回值。
十二、撰写 libcudf 注释的快速自查清单
结合全文,为新增或修改 API 补充注释时可按以下清单逐项核对:
- 许可证头用
/*(而非/**),年份区间正确; - 文档块用
/** ... */,紧贴声明之前,行内格式为"空格+星号+空格"; @brief一句话简介,后跟空行;详细描述覆盖输入输出关系、null 处理、边界与性能;- 标签顺序:
@throw→@tparam→@param→@return,参数名与签名完全一致,必要时标注[in]/[out]/[in,out]; - 模板参数、枚举值、getter/setter 用
///<行尾注释,复杂成员用完整块注释; - 需要分组时:新 API 用
@ingroup <组ID>,或文件内用@addtogroup+@{ ... @}(放在命名空间花括号内); - 避免重复文档,用
@copydoc复用(含完整签名); - 弃用 API 用
@deprecated并注明替代方案; - 示例代码用
@code/@endcode,类名写完全限定形式; - 提交前确认本机 Doxygen 为 1.18.0,并运行 ci/checks/doxygen.sh 风格的校验,确保无文档告警。
遵循这套规范写出的注释,既能让源码在编辑器里整洁易读,又能被 Doxygen 稳定地转换成结构清晰、可检索、可引用的 API 文档——这正是 libcudf 这样一个类型繁多、API 庞大的 GPU 计算库保持文档高质量的关键工程实践。
- 数据分析
- 数据工程
- 机器学习
【免费下载链接】cudf
cuDF - GPU DataFrame Library
相关推荐
算法文档编写规范Algorithms39:专业注释与API文档标准指南
算法文档编写规范Algorithms39:专业注释与API文档标准指南 在算法开发中,规范的文档编写是确保代码可维护性和团队协作效率的关键。Algorithms
示例工程OpenCV项目文档编写指南:Doxygen文档生成与编写规范
OpenCV项目文档编写指南:Doxygen文档生成与编写规范 概述 在OpenCV项目中,文档是帮助开发者理解和使用库功能的重要资源。本文将详细介绍如何使用D
计算机视觉图像处理深度学习机器学习Rerun C++ SDK 文档写作指南:Doxygen 注释规范、本地构建与版本化发布工作流
Rerun C++ SDK 文档写作指南:Doxygen 注释规范、本地构建与版本化发布工作流 Rerun C++ SDK 的 API 文档由 Doxygen
数据可视化3D渲染数据分析
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考