开发与贡献指南

首先,感谢你的参与和贡献!我们欢迎一切形式的贡献,包括但不限于

  • 修复 BUG
  • 提出和实现新功能
  • 对本文档进行改进和翻译(欢迎前往 Taichi 中文文档
  • 完善错误时的提示,使之对用户更友好
  • 提交新的测试用例
  • 提交新的样例程序
  • 提交编译器性能补丁
  • 发布有关 Taichi 的博客文章和教程
  • 加入我们的 Taichi 论坛
  • 向你的朋友们介绍 Taichi 或者直接在 GitHub 上星标 Taichi 也可以。
  • 修复文档,代码,注释中的拼写错误(像这样的小问题请直接创建一个 PR 而不必开一个 issue)

如何参与 BUG 修复,添加新特性

标记了 “good first issue” 的 issue 是较容易上手的。

  • 请先在这个 issue 中留下一句评论(比如: 我知道怎么解决这个,我乐意帮忙! )。这样大家就知道已经有人在解决这个问题了,从而避免重复劳动;
  • 如果没有核心开发成员说明一个 issue 可能的解决方案,请简要地描述你的方案,并在你开始之前静候开发成员的回复,从而保障实现的简洁高效。

标记了 “welcome contribution” 的 issue 相比之下更有挑战性但对新手仍然是比较友好的。

进阶指导

  • 切实解决问题是我们的最终目标。
  • 不要小题大做:用 简单 的方案去解决简单的问题,这样你可以抽出时间和精力处理那些真正困难的问题。
  • 几乎每一个设计都有两面性。如果利大于弊,那就可以看作是一个 好的决定 ,请务必权衡利弊。
  • 调试是很困难的,每一次的改动应该很小,这样 BUG 的源头就可以很容易地找到。
  • 单元/集成测试是我们的好伙伴。

注解

“软件设计过程中中存在两种模式:一种是使之结构简单明了到没有任何问题,另一种是令结构设计足够复杂到完美无缺 而第一种方案则要困难的多.” — C.A.R. 霍尔

需要记住的一点是,Taichi 最初作为一个学术研究项目而诞生。这通常意味着有些部分没有机会经过稳固坚实的设计。虽然我们一直在努力提高代码质量,但这并不意味着项目能没有技术负债。有些地方仍可能会过于复杂而让人感到困惑。一旦你发现这种情形的存在,非常欢迎给我们提出 PR!:-)

高效率地沟通

  • 传达了多少有效信息,比打了多少字重要的多。
  • 在沟通中保持积极,礼貌,注意语言的组织性、准确性。
  • 注意除了文字之外,列表(Bulleted lists)也是我们表达过程中的好伙伴。
  • 提交评论前请仔细预读:如果你是读者,你能读懂自己所写的内容么?
  • 如果你的母语不是英语,考虑使用拼写检查器,如 Grammarly

请根据事实进行讨论与反馈,而不是个人感觉。对我们所有人来说,保持一个友好、零责备的社区环境是非常重要的。一些例子如下:

小技巧

(可接受的表达方式)这种设计可能会让 Taichi 的初学者感到困惑。

警告

(不可接受的表达方式)这种设计真是太糟糕了。

提交良好的 PR

  • 我们鼓励改动很小的 PR, 一个 PR 理想情况下应该 只针对一个问题(issue)
    • 也可以掺杂一些 无关紧要 的优化重构,比如修正笔误;
    • 审稿人保留要求 PR 作者删除一些 无关紧要 的改动的权利。
  • PR 中的所有 commit 都应被 压缩&合并到 master 分支的一个 commit 里
  • 为保留清晰的提交日志 PR 作者 不应该将多条 commit 压缩(squash)后提交;
  • 当实现一个复杂的特性时,考虑将其分散为许多个小 PR,从而保证更具细节的开发时间线,保证与开发者更频繁的沟通。
  • 如果你想更及时的得到核心开发成员的反馈
    • 通过 GitHub 的 Draft 状态开一个 PR,这样就可以和我们实时分享你的进展了;
    • 请确保在评论中 @ 相应开发成员,或者使用请求评审(request the review)。
  • 如果你同时在处理多个 PR
    • 互不依赖的 PR 都应该是基于 master 衍生出的 不同 分支;
    • 互相依赖的 PR 应该在所有前置 PR 合并入 master 后再进行提出。
  • 所有 PR 理想情况下都应该伴随着相应的 测试;
  • 除了内部编译器的实现外,其余的 PR 都应该带有与其功能相对应的 文档更新(documentation update);
  • 所有 PR 必须通过 持续集成测试(continuous integration tests) 后才能被合并;
  • PR 的标题应当按照 PR title format and tags 的要求编写;
  • 除此之外,谷歌有篇相当棒的文章 how to have your PR merged quickly. [PDF] 可供参考

审核与 PR 的合并

持续集成的运用

  • 持续集成(Continuous Integration,CI),将在 CI 环境中 构建(build)测试(test) 你所提交的 PR。
  • 目前,Taichi 使用的集成测试服务是 Travis CI (OS X 和 Linux 平台)和 AppVeyor (Windows 平台)。
  • 每次你推送提交到一个开着的 PR 时,CI 将被触发。
  • 可以在提交消息前加上 [skip ci] 以避免触发 CI。例如:``[skip ci] This commit will not trigger CI`
  • 提交 ID 右侧有绿色对勾表示 CI 通过,红色叉号表示 CI 失败。

规范代码结构

  • 在本地,可以通过在命令行中运行 ti format 来自动格式化代码。请注意,在使用 ti format 之前,您必须在本地安装 clang-format-6.0yapf v0.29.0

  • 如果不想在本地安装这些格式化工具,也可以使用 格式化服务器(format server) 。这是个 ti format 的在线版本。

    • 访问 http://kun.csail.mit.edu:31415/,并点击选取所需格式化的 PR id。
    • 回到 PR 页面,你将看到一个名为 @taichi-gardener (机器人) 的用户推送了一个名为 [skip ci] enforce code format 的提交。
    • 如果你没能找到机器人的提交,说明没有发现任何不规范的代码格式,。
    • 然后在本地分支中运行 git pull 来提取格式化代码。
    • 值得留意的是,备注带有为 [format] 的提交信息将自动触发格式化服务。例如:[format] our commit message

PR 标题格式和标签

PR 标题将成为 master 分支中提交历史的一部分,因此保证 PR 标题的可读性非常重要。

  • 请务必在 PR 标题前附加上至少一个标签,如 [Metal] 等:

    • 当使用多个标签时,确保标签之间只留有一个空格分隔;
    • 例如, [Metal][refactor]``(没有空格)应该被格式化为 ``[Metal] [refactor];
  • PR 标题主干部分的首字母应该大写:

    • 例如, [doc] improve documentation 应该被格式化为 [doc] Improve documentation;
    • 同时, [Lang] “ti.sqr(x)” is now deprecated 是可以的,因为 是一个符号。
  • 请不要在 PR 标题中包括反引号 (“`”)。

  • 例如,“[Metal] Support bitmasked SNode”,“[OpenGL] AtomicMin/Max support”,或 “[Opt] [IR] Enhanced constant folding”。

常用的标签:

  • [Metal], [OpenGL], [CPU], [CUDA]: backends;
  • [LLVM]: CPU 和 CUDA 共享的 LLVM 后端;
  • [Lang]: 前端语法特性,包括语法糖;
  • [Std]: 标准库,例如 ti.Matrixti.Vector;
  • [IR]: 中间表示(intermediate representation, IR);
  • [Opt]: IR 优化迭代轮数;
  • [GUI]: 内嵌的 GUI 系统;
  • [Refactor]: 代码重构优化;
  • [CLI]: 命令行接口, 例如 ti 命令;
  • [Doc]: 与 docs/ 目录下的文档相关;
  • [Example]: 与 examples/ 目录下的样例程序相关;
  • [Test]: 与 tests/ 目录下增加和改进测试程序相关;
  • [Linux]: 与Linux 平台有关;
  • [Mac]: 与Mac OS X 平台有关;
  • [Windows]: 与Windows 平台有关;
  • [Perf]: 性能改进;
  • [Misc]: 难以归类的杂项,如版本跳跃,格式优化;
  • [Bug]: 修复 Bug;
  • misc/prtags.json 中查看更多标签.
  • 在引进新标签时,请在首先使用该标签的 PR 中一并更新 misc/prtags.json 列表,以便其余成员跟随使用。

注解

我们感谢所有的贡献,但是我们不应该把每一个 PR 的标题暴露给终端用户。因此,应该将变更日志分类成 用户应该知道什么开发人员正在做什么 是必要的。而这是通过 大写 PR 标签 区分的:

  • 对用户可见/值得注意的 PR,应该将其一开始的标签以 大写的首字母 进行标记,例如 [Metal], [OpenGL], [IR], [Lang], [CLI]。在发布新版本时,脚本(python/taichi/make_changelog.py)将生成一个突出显示这些更改(PR 标题)的变更日志。因此,确保终端用户能够理解你的 PR 所做的工作是非常 重要 的,而这都是 基于你的PR标题
  • 其他类型的 PR(底层开发/中间实现)应该使用 全小写字母 的标签 :例如 [metal], [opengl], [ir], [lang], [cli]
  • 由于发布更新日志的生成方式,PR 标题中应该 最多只有一个大写标记,以防止重复的 PR 突出显示。例如, [GUI] [Mac] Support modifier keys (#1189) 就是一个反例, 我们应该用 [gui] [Mac] Support modifier keys in GUI 来替代。 请只大写与 PR 内容最相关的标签。

C++ 和 Python 标准

Taichi 的 C++ 模块是基于 C++ 17编写的,Python 模块是基于3.6+编写的。所以你可以合理认为 C++ 17和 Python 3.6 特性总是可用的。

Taichi 编译器的开发建议

阅读 Life of a Taichi kernel 这一章也许有助于你理解我们的工作。它解释了整个编译过程。

如果你的工作涉及 IR 优化,请参见 基准测试和回归测试

使用 ti.init(arch=desired_arch, **kwargs) 创建 Taichi 程序时,传入以下参数,可以使 Taichi 编译器打印出 IR:

  • print_preprocessed = True:打印前端 Python AST 转换的结果。结果脚本(resulting scripts)在执行时将生成一个 Taichi 前端 AST。
  • print_ir = True:打印内核编译(不包括访问器)中的 Taichi IR 转换过程。
  • print_accessor_ir = True:打印数据访问器的 IR 转换过程,这是一种特殊而简单的内核信息。(不过很少使用,除非你正在调试数据访问器相关的编译)
  • print_struct_llvm_ir = True: 保存由 Taichi 结构编译器生成的 LLVM IR。
  • print_kernel_llvm_ir = True: 保存由 Taichi 内核编译器生成的 LLVM IR。
  • print_kernel_llvm_ir_optimized = True:保存每个内核优化的 LLVM IR。
  • print_kernel_nvptx = True:保存每个内核生成的 NVPTX(仅限 CUDA)。

注解

Python 作用域中的数据访问器被实现为特殊的 Taichi 内核。例如,x[1, 2, 3] = 3 将调用 x 的写访问器内核, print(y[42]) 将调用 y 的读访问器内核。

目录结构

关键文件包括

  • taichi: 核心编译器实现

    • program: 上层结构
    • ir: 中间表示
    • analysis: 静态分析
    • transforms: IR 转换传递(Passes)
    • inc: Small definition files to be included repeatedly
    • jit: Just-In-Time compilation base classes
    • llvm: LLVM utilities
    • runtime: LLVM runtime environments
    • struct: 结构编译器基类
    • codegen: 代码生成基类
    • backends: 基于设备的代码生成/运行时环境
      • cpu: CPU backend implementation
      • cuda: CUDA backend implementation
      • opengl: OpenGL backend implementation
      • metal: Metal backend implementation
      • cc: C backend implementation (WIP)
    • gui: GUI system
    • math: Math utilities
    • python: C++/Python 接口
    • platform: 平台支持依赖
    • system: 操作系统相关的基础结构
    • util: 各种各样的工具
  • python/taichi: Python frontend implementation

    • core: Loading & interacting with Taichi core
    • lang: Python-embbed Taichi language & syntax (major)
    • misc: Miscellaneous utilities
    • tools: Handy end-user tools
  • tests: Functional tests

    • python: Python tests (major)
    • cpp: C++ tests
  • examples: 样例程序

  • docs: 文档

  • benchmarks: 性能基准

  • external: External libraries

  • misc: 零散(但仍很有用)的文件

测试

Tests should be added to tests/.

Command line tools

  • 通过使用 ti test 运行所有测试实例。
  • 通过使用 ti test -v 查看详细输出信息。
  • 通过使用 ti test -C 运行测试并记录代码覆盖率, 参阅 Code coverage 查看更多信息.
  • Use ti test -a <arch(s)> for testing against specified backend(s). e.g. ti test -a cuda,metal.
  • Use ti test -na <arch(s)> for testing all architectures excluding some of them. e.g. ti test -na opengl,x64.
  • Use ti test <filename(s)> to run specific tests in filenames. e.g. ti test numpy_io will run all tests in tests/python/test_numpy_io.py.
  • Use ti test -c to run only the C++ tests. e.g. ti test -c alg_simp will run tests/cpp/test_alg_simp.cpp.
  • Use ti test -k <key> to run tests that match the specified key. e.g. ti test linalg -k "cross or diag" will run the test_cross and test_diag in tests/python/test_linalg.py.

或者使用 ti test -h 查看更多选项。

For more details on how to write a test case, see write_test.

文档

Documentations are put under the folder docs/.

  • We use reStructured text (.rst) to write documentation.
  • We host our documentation online using readthedocs.io.
  • 使用 ti doc 建立本地文档。
  • Open the documentation at docs/build/index.html.

注解

在 Linux/OS X 下, 使用 watch -n 1 ti doc 以持续地构建文档。

如果 OpenGL 后端检测器一直在创建新窗口,对 ti doc 附加执行 export TI_WITH_OPENGL=0

跨 Python/C++ 的高效代码导航

如果你使用的是前端语言(Python/C++接口),要在代码库中导航,ffi-navigator 允许从 Python 跳转到它们在 C++ 中绑定的定义。按照前述链接的 README 设置你的编辑器。

升级 CUDA

目前我们的开发工作是针对 CUDA 10。在升级 CUDA 版本时,当前 external/cuda_libdevice/slim_libdevice.10.bc 文件应该被新的版本的所取代。

To generate the slimmed version of libdevice based on a full libdevice.X.bc file from a CUDA installation, use ti task make_slim_libdevice [libdevice.X.bc file]