编码指南

目录

C++ 编码指南

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

    • 每行文本最多可包含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 Vignettes

Rmarkdown vignettes 放置在 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)是了解其工作原理的最佳方式。在 tests/ci_buildR-package/tests/helper_scripts 中有辅助脚本,用于运行包括 linter 在内的各种检查,并生成标准的 tarball。

本地运行格式检查

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

Linter

我们使用一系列的代码检查工具来强制执行风格规范并发现潜在错误。对于像Python这样的脚本语言,代码检查尤为有用,因为它能捕捉到许多原本会在运行时发生的错误。

对于Python脚本,使用 pylintblackisort 来提供编码风格指导,并且需要 mypy 进行类型检查。对于C++,使用 cpplint 以及 clang-tidy。对于R,使用 lintr

要在本地运行Python检查,请安装前面提到的检查器并运行:

cd /path/to/xgboost/
python ./tests/ci_build/lint_python.py --fix

要运行 R 的检查:

cd /path/to/xgboost/
Rscript tests/ci_build/lint_r.R $(pwd)

要在本地运行 cpplint 检查:

cd /path/to/xgboost/
python ./tests/ci_build/lint_cpp.py

有关 clang-tidy 的详细信息,请参见下一节。对于 CMake 脚本:

bash ./tests/ci_build/lint_cmake.sh

最后,jvm-packages 的 linter 被集成到 maven 构建过程中。

Clang-tidy

Clang-tidy 是一个由 LLVM 团队制作的 C++ 代码的高级 linter。我们使用它来使我们的 C++ 代码库符合现代 C++ 实践和约定。

要在本地运行此检查,请从顶级源代码树运行以下命令:

cd /path/to/xgboost/
python3 tests/ci_build/tidy.py

此外,脚本接受两个可选的整数参数,即 --cpp--cuda。默认情况下,它们都设置为 1,这意味着将检查 C++ 和 CUDA 代码。如果 CUDA 工具包未安装在您的机器上,您将遇到错误。要排除 CUDA 源代码的检查,请使用:

cd /path/to/xgboost/
python3 tests/ci_build/tidy.py --cuda=0

同样地,如果你想从代码检查中排除 C++ 源代码:

cd /path/to/xgboost/
python3 tests/ci_build/tidy.py --cpp=0

处理用户输入数据的指南

这是一个处理用户输入数据的非全面指南。XGBoost 支持多种原生数据结构,主要来自高级语言绑定。输入范围从基本的连续一维内存缓冲区到更复杂的数据结构,如带有有效性掩码的列数据。原始输入数据可以在两个地方使用,首先是各种 DMatrix 的构建,其次是就地预测。对于普通的内存缓冲区,没有太多可讨论的,因为它只是一个带有大小的指针。但对于一般的 n 维数组和列数据,有许多细微差别。XGBoost 有 3 种不同的数据结构来处理可选掩码数组(张量),对于消费用户输入,应选择 ArrayInterface。由于历史原因(XGBoost 最初是一个简单得多的库,当时并不太关心内存使用),有许多现有函数只接受普通指针。ArrayInterface__array_interface__ 协议(由 numpy 定义)或 ``__cuda_array_interface__``(由 numba 定义)的内存表示。以下是接受相关用户输入时需要牢记的事项清单:

  • [ ] 它是跨步的吗?(由 strides 字段标识)

  • [ ] 如果是向量,它是行向量还是列向量?(通过 shapestrides 识别)。

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

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

  • [ ] 某些维度是否微不足道?(shape[dim] <= 1)

  • [ ] 它有掩码吗?(通过 mask 字段识别)

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

  • [ ] 它是在CUDA内存中吗?(通过 data 字段识别,并可选地通过 stream 字段)

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

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