开发与贡献指南
首先,感谢你的参与和贡献!我们欢迎一切形式的贡献,包括但不限于
- 修复 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 理想情况下都应该伴随着相应的 测试;
- 除了内部编译器的实现外,其余的 PR 都应该带有与其功能相对应的 文档更新(documentation update);
- 所有 PR 必须通过 持续集成测试(continuous integration tests) 后才能被合并;
- PR 的标题应当按照 PR title format and tags 的要求编写;
- 除此之外,谷歌有篇相当棒的文章 how to have your PR merged quickly. [PDF] 可供参考
审核与 PR 的合并
- 请按照以下几个来自谷歌的建议
- 合并操作应当始终将 PR 压缩&合并 到主分支(默认为master)上;
- 主分支要求记录 线性历史;
- 确保 PR 能够顺利通过 持续集成测试,文档更新等情况除外;
- 确保标题遵循 PR title format and tags 的要求。
持续集成的运用
- 持续集成(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.0
和yapf 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]
: 后端;[LLVM]
: CPU 和 CUDA 共享的 LLVM 后端;[Lang]
: 前端语法特性,包括语法糖;[Std]
: 标准库,例如ti.Matrix
和ti.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
: 需要被重复引用的定义文件jit
: 实时(Just-in-Time)编译器基类llvm
: LLVM 实用工具runtime
: LLVM 运行环境struct
: 结构编译器基类codegen
: 代码生成基类backends
:基于设备的代码生成/运行时环境cpu
:CPU 后端实现cuda
:CUDA 后端实现opengl
:OpenGL 后端实现metal
:Metal 后端实现cc
:C 后端实现 (WIP)
gui
:GUI 系统math
:数学类使用工具python
: C++/Python 接口platform
: 平台支持依赖system
: 操作系统相关的基础结构util
:各种各样的工具
python/taichi
:Python 前端实现core
:Taichi 核心的加载 & 交互lang
: 嵌入在 Python 中的 Taichi 语言 & 语法(重要)misc
:各种各样的工具tools
:提供给终端用户的便捷工具
tests
:功能测试python
:Python 测试(重要)cpp
:C++ 测试
examples
: 样例程序docs
: 文档benchmarks
: 性能基准external
:扩展库misc
: 零散(但仍很有用)的文件…
测试
测试程序应该添加到 tests/
文件夹下。
命令行工具
- 通过使用
ti test
运行所有测试实例。 - 通过使用
ti test -v
查看详细输出信息。 - 通过使用
ti test -C
运行测试并记录代码覆盖率,参阅 Code coverage 查看更多信息。 - 使用
ti test -a <arch(s)>
针对指定后端进行测试。例如,ti test -a cuda,metal
。 - 使用
ti test -na <arch(s)>
测试除指定架构外的其余所有架构。例如,ti test -na opengl,x64
。 - 使用
ti test <filename(s)>
运行指定文件名的测试实例。例如,ti test numpy_io
将会在tests/python/test_numpy_io.py
中运行所有测试。 - 使用
ti test -c
运行 C++ 测试程序。例如,ti test -c alg_simp
将会运行tests/cpp/test_alg_simp.cpp
。 - 使用
ti test -k <key>
运行与指定关键词相匹配的测试。例如,ti test linalg -k "cross or diag"
将会在tests/python/test_linalg.py``中运行 ``test_cross
和test_diag
。
或者使用 ti test -h
查看更多选项。
更多有关如何编写测试用例的详细信息,请参阅 write_test。
文档
[Doc]
:与 docs/
目录下的文档相关。
- 我们使用 reStructured text (.rst) 来编写文档。
- 我们使用 readthedocs.io 在线托管我们的文档。
- 使用
ti doc
建立本地文档。 - 在
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
文件应该被新的版本的所取代。
基于 CUDA 安装时完整的 libdevice.X.bc
文件,使用 ti task make_slim_libdevice [libdevice.X.bc file]
生成精简版的 libdevice