编码指南
目录
C++ 编码指南
遵循C++ 的 Google 风格,但有两个例外
每行文本最多可包含 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 编码指南。
Docstrings 应采用NumPy docstring 格式。
R 编码指南
代码风格
我们遵循 Google 的 C++ 风格指南编写 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
创建拉取请求。您也可以在
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 actions)。ops/script
和 R-package/tests/helper_scripts
中有辅助脚本,用于运行各种检查,包括 linter 和制作标准 tarball。
在本地运行格式检查
一旦您向 dmlc/xgboost 提交拉取请求,我们会执行两项自动检查以强制执行编码风格约定。为了加快代码审查过程,我们鼓励您在提交拉取请求之前在自己的机器上本地运行检查。
代码检查器 (Linter)
我们结合使用多种代码检查器来强制执行风格约定并发现潜在错误。代码检查对于 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-packages 的代码检查器已集成到 maven 构建过程中。
Clang-tidy
Clang-tidy 是 LLVM 团队制作的 C++ 代码高级代码检查器。我们使用它来使我们的 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 矩阵行进行切片应根据各自的步幅计算每个字段的偏移量。
对于标签等元信息,其大小和复杂性都在增长,我们目前只接受掩码数组(没有专门的适配器)。应注意输入数据的形状。如果将来我们有多个目标,基准边距可以是 2 维或更高。目前 DMatrix
中的 getter 只返回 1 维扁平向量,这在将来需要时可以改进。