编码指南

目录

C++ 编码指南

  • 遵循C++ 的 Google 风格,但有两个例外

    • 每行文本最多可包含 100 个字符。

    • 允许使用 C++ 异常。

  • 使用 C++17 特性,例如智能指针、大括号初始化器、lambda 函数和 std::thread

  • 使用 Doxygen 文档化所有接口代码。

  • 我们对头文件导入的符号有一些注释,其中一些由 include-what-you-use 提示。这不是必需的。

  • 我们使用 clang-tidy 和 clang-format。您可以在 XGBoost 源代码树的根目录中查看它们的配置。

  • 我们有一系列自动检查,以确保我们的所有代码库都符合 Google 风格。在提交拉取请求之前,我们鼓励您在自己的机器上运行风格检查。参见 R 编码指南

Python 编码指南

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/scriptR-package/tests/helper_scripts 中有辅助脚本,用于运行各种检查,包括 linter 和制作标准 tarball。

在本地运行格式检查

一旦您向 dmlc/xgboost 提交拉取请求,我们会执行两项自动检查以强制执行编码风格约定。为了加快代码审查过程,我们鼓励您在提交拉取请求之前在自己的机器上本地运行检查。

代码检查器 (Linter)

我们结合使用多种代码检查器来强制执行风格约定并发现潜在错误。代码检查对于 Python 等脚本语言特别有用,因为我们可以捕获许多原本会在运行时发生的错误。

对于 Python 脚本,pylintblackisort 用于提供编码风格指导,mypy 用于类型检查。对于 C++,cpplintclang-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 字段标识)

  • [ ] 如果是向量,它是行向量还是列向量?(由 shapestrides 共同标识)。

  • [ ] 数据类型是否受支持?半精度类型和 128 位整数类型应在进入 XGBoost 之前进行转换。

  • [ ] 它是否具有高于 1 的维度?(由 shape 字段标识)

  • [ ] 某些维度是否是平凡的?(shape[dim] <= 1)

  • [ ] 它有掩码吗?(由 mask 字段标识)

  • [ ] 掩码可以广播吗?(目前不支持)

  • [ ] 它在 CUDA 内存上吗?(由 data 字段标识,可选 stream)

大多数检查由 ArrayInterface 在构建期间处理,除了数据类型问题,因为它不知道如何使用 C 内置类型转换此类指针。但出于安全原因,仍应尝试为所有项目编写相关测试。数据类型问题应在每个特定数据输入的语言绑定中处理。对于单块列式格式,它只是每列的掩码数组,因此应与普通数组统一处理。对于输入预测器 X,我们为每种输入类型提供适配器。有些是其他适配器的组合。例如,CSR 矩阵有 3 个潜在的带步幅数组,分别用于 indptrindicesvalues。不应对这些组件做任何假设(所有复选框都应考虑)。对 CSR 矩阵行进行切片应根据各自的步幅计算每个字段的偏移量。

对于标签等元信息,其大小和复杂性都在增长,我们目前只接受掩码数组(没有专门的适配器)。应注意输入数据的形状。如果将来我们有多个目标,基准边距可以是 2 维或更高。目前 DMatrix 中的 getter 只返回 1 维扁平向量,这在将来需要时可以改进。