编码指南
目录
C++ 编码指南
遵循Google C++ 风格指南,有两个例外
每行文本最多包含 100 个字符。
允许使用 C++ 异常。
使用 C++17 特性,例如智能指针、大括号初始化器、lambda 函数和
std::thread
。使用 Doxygen 文档化所有接口代码。
我们对头文件导入的符号有一些注释,其中一些由 include-what-you-use 提示。这不是必需的。
我们使用 clang-tidy 和 clang-format。您可以在 XGBoost 源码树的根目录中查看它们的配置。
我们有一系列自动化检查,以确保我们的所有代码库符合 Google 风格。在提交拉取请求之前,建议您在本地机器上运行风格检查。请参阅 R 编码指南。
Python 编码指南
遵循PEP 8:Python 代码风格指南。我们使用 Pylint 自动化地在整个 Python 代码库中强制执行 PEP 8 风格。在提交拉取请求之前,建议您在本地机器上运行 Pylint。请参阅 R 编码指南。
文档字符串应采用NumPy 文档字符串格式。
R 编码指南
代码风格
对于 C++ 代码,我们遵循 Google 的 C++ 风格指南。
这主要是为了与项目的其余部分保持一致。
另一个原因是我们可以使用 linter 自动检查风格。
必要时,您可以使用
// NOLINT(*)
注释禁用特定行的 linter 警告。我们使用 roxygen 文档化 R 包。
Rmarkdown 插图教程
Rmarkdown 插图教程位于 R-package/vignettes 中。这些 Rmarkdown 文件未编译。我们将编译版本托管在 doc/R-package 中。
添加新的 Rmarkdown 插图教程需要遵循以下步骤
将原始 Rmarkdown 文件添加到
R-package/vignettes
中。修改
doc/R-package/Makefile
,添加要构建的 markdown 文件。将 dmlc/web-data 仓库克隆到
doc
文件夹。现在在
doc/R-package
目录下输入以下命令make the-markdown-to-make.md
这将生成 markdown 文件以及
doc/web-data/xgboost/knitr
中的图表。修改
doc/R-package/index.md
,使其指向生成的 markdown 文件。将生成的图表添加到
dmlc/web-data
仓库中。如果您已经将仓库克隆到 doc 目录下,这意味着执行
git add
为 markdown 文件和
dmlc/web-data
创建拉取请求 (PR)。您也可以在
doc
目录下输入以下命令在本地构建文档make html
我们这样做是为了避免因生成的图像而导致仓库大小爆炸式增长。
R 包版本控制
请参阅 XGBoost 发布策略。
使用不同编译器测试 R 包
您可以通过修改主目录中的配置文件来更改 R 的默认编译器。例如,如果您想在 Linux 上测试使用 clang++ 而非 g++ 构建的 XGBoost,请将以下内容放入您的 ~/.R/Makevars
文件中
CC=clang-15
CXX17=clang++-15
请注意,变量名应与 R CMD
使用的名称匹配
R CMD config CXX17
在 R 中注册原生例程
根据R 扩展手册,注册原生例程并禁用符号搜索是良好的实践。当对 R 包的 C++ 接口进行任何更改或添加时,请同时在 src/init.c
中进行相应的更改。
生成包并运行测试
XGBoost 的源码布局与正常的 R 包有些不同,因为 XGBoost 主要用 C++ 编写,同时考虑了多种语言绑定。因此,生成标准的 R tarball 需要采取一些特殊的措施。大多数测试都在 CI 上运行,因此了解其工作原理的最佳方式是查看 CI 配置文件(撰写本文时是 GitHub action)。在 ops/script
和 R-package/tests/helper_scripts
中有辅助脚本,用于运行各种检查,包括 linter 和制作标准 tarball。
本地运行格式检查
一旦您向 dmlc/xgboost 提交拉取请求,我们将执行两项自动检查以强制执行编码风格规范。为了加快代码审查过程,建议您在提交拉取请求之前在本地机器上运行这些检查。
Linter
我们结合使用多种 linter 来强制执行风格规范并查找潜在错误。Linting 对于 Python 等脚本语言特别有用,因为我们可以捕获许多原本会在运行时发生的错误。
对于 Python 脚本,使用 pylint、black 和 isort 提供编码风格指导,并且需要 mypy 进行类型检查。对于 C++,使用 cpplint 和 clang-tidy
。对于 R,使用 lintr
。
要在本地运行 Python 检查,请安装前面提到的检查工具并运行
cd /path/to/xgboost/
python ./ops/script/lint_python.py --fix
要运行 R 检查
cd /path/to/xgboost/
R CMD INSTALL R-package/
Rscript ops/script/lint_r.R $(pwd)
要在本地运行 cpplint 检查
cd /path/to/xgboost/
python ./ops/script/lint_cpp.py
有关 clang-tidy,请参阅下一节。对于 CMake 脚本
bash ./ops/script/lint_cmake.sh
最后,jvm 包的 linter 已集成到 maven 构建过程中。
Clang-tidy
Clang-tidy 是一个由 LLVM 团队开发的 C++ 代码高级 linter。我们使用它来使我们的 C++ 代码库符合现代 C++ 实践和约定。
要在本地运行此检查,请在顶级源码树中运行以下命令
cd /path/to/xgboost/
python3 ops/script/run_clang_tidy.py
此外,该脚本接受两个可选的整数参数:--cpp
和 --cuda
。默认情况下,它们都设置为 1,表示将同时检查 C++ 和 CUDA 代码。如果您的机器上未安装 CUDA 工具包,将会遇到错误。要从 linting 中排除 CUDA 源码,请使用
cd /path/to/xgboost/
python3 ops/script/run_clang_tidy.py --cuda=0
类似地,如果您想从 linting 中排除 C++ 源码
cd /path/to/xgboost/
python3 ops/script/run_clang_tidy.py --cpp=0
用户输入数据处理指南
这是一份关于处理用户输入数据的非全面指南。XGBoost 本身支持多种数据结构,它们大多来自更高级的语言绑定。输入范围从基本的连续一维内存缓冲区到更复杂的数据结构,例如带有有效性掩码的列式数据。原始输入数据可以在两个地方使用:首先是构建各种 DMatrix
,其次是原地预测。对于纯内存缓冲区,由于它只是一个带有大小的指针,所以没有太多可讨论的。但对于一般的 n 维数组和列式数据,存在许多细微之处。XGBoost 有 3 种不同的数据结构用于处理可选掩码数组(张量),对于消费用户输入,应选择 ArrayInterface
。由于历史原因(XGBoost 起初是一个简单得多的库,当时不太关心内存使用),许多现有函数只接受纯指针。ArrayInterface
是 numpy 定义的 __array_interface__
协议或 numba 定义的 __cuda_array_interface__
的内存表示。以下是接受相关用户输入时需要牢记的检查清单
[ ] 它是否是跨步的?(由
strides
字段标识)[ ] 如果它是一个向量,它是行向量还是列向量?(由
shape
和strides
共同标识)[ ] 数据类型是否支持?半精度类型和 128 位整数类型在进入 XGBoost 之前应进行转换。
[ ] 它是否具有高于 1 的维度?(由
shape
字段标识)[ ] 是否有一些维度是平凡的?(shape[dim] <= 1)
[ ] 它是否有掩码?(由
mask
字段标识)[ ] 掩码是否可以广播?(目前不支持)
[ ] 它是否在 CUDA 内存上?(由
data
字段以及可选的stream
标识)
大多数检查在构建 ArrayInterface
时处理,但数据类型问题除外,因为它不知道如何将这些指针转换为 C 内置类型。但为了安全起见,仍应尝试为所有条目编写相关测试。数据类型问题应在每种特定数据输入的语言绑定中处理。对于单块列式格式,它只是每列的掩码数组,因此应统一视为普通数组。对于输入预测器 X
,我们为每种输入类型提供了适配器。有些是其他适配器的组合。例如,CSR 矩阵有 3 个潜在的跨步数组,分别用于 indptr
、indices
和 values
。不应对这些组件做任何假设(应考虑所有检查项)。对 CSR 矩阵的行进行切片时,应根据各自的步长计算每个字段的偏移量。
对于标签等元信息(其大小和复杂性都在增长),目前我们只接受掩码数组(没有专门的适配器)。应注意输入数据的形状。对于基础边距 (base margin),如果将来有多个目标,它可能是 2 维或更高维。目前 DMatrix
中的 getter 只返回一维扁平向量,这可以在将来需要时改进。