小编注:本文由姚班大佬胡渊鸣博士在知乎首发,小编经大佬同意发布在此公众号上
(发私信还担心被无视,没想到很快就收到同意的回复)。近日大佬成立公司专门做图形学方面的开源工作,小编作为图形学爱好者甚是激动
,希望后面能看到特别的成果,也希望能让这种开源好项目被更多人知道,另外想看工作机会的算法小能手可以考虑呀,国内最强顶配团队
!以下为正文。
文:胡渊鸣 译、校:胡渊鸣 匡冶
作者简介
胡渊鸣是太极图形联合创始人兼CEO,麻省理工学院计算机科学博士(2021年3月通过MIT EECS Ph. D. thesis defense)。主要研究方向为计算机图形学与编译器。胡渊鸣博士2017年7月本科毕业于清华大学姚班。
匡冶是太极图形联合创始人兼CTO,前Google软件工程师。匡冶拥有哈佛大学计算机科学硕士学位、清华大学学士学位。
太极图形(Taichi Graphics) 公司主导开发开源基础设施太极(Taichi)编程语言,专注图形学、编译器、高性能计算、人工智能相关技术,拥有世界领先的技术积累。团队成员均来自麻省理工学院、哈佛、宾夕法尼亚大学、清华、Google、微软等顶尖学府和企业,54% 成员本科毕业于清华大学,人均 IOI/NOI/ACM-ICPC 金牌数 >= 2。团队旨在打造图形计算、物理仿真的标准基础设施,让图形编程惠及每一人。
加盟热线:hr@taichi.graphics
导言
从图像处理、真实感渲染到物理模拟、VR/AR,视觉计算(visual computing) 任务在当今社会已经无处不在,成为连接物理世界与数字世界的桥梁。挑战与机遇并存,视觉计算任务通常需要极高的性能以处理海量数据,如视频、粒子、体积、网格等。研究者和开发人员投入了大量精力去设计和优化相关的视觉计算硬件(多核CPU、大规模并行GPU、以及DSP)、软件(如渲染器及物理引擎等)和算法(如快速傅里叶变换、多重网格算法等)。这里的讨论集中于视觉计算的软件开发部分。
本系列文章部分译自胡渊鸣同学的在麻省理工学院的博士论文 "The Taichi High-Performance and Differentiable Programming Language for Sparse and Quantized Visual Computing", 意在回顾、探讨Taichi视觉计算编程语言的一系列工作。话题包括:
动机与工程实践 (本文)
- 为什么我们需要新的编程语言
- Taichi语言及其目标
- Taichi的实用主义设计决策
- 三类新的语言抽象与编译器优化
反思
- 与其他编程系统的关系
- 我们学到了什么
- 未来工作
为什么需要新的视觉计算编程语言?
高度优化的视觉计算软件系统通常用相对接近硬件的语言(如C 、CUDA、GLSL等)来实现。这些语言提供了编写高性能程序的可能性。不幸的是,仅仅使用 (而不进一步优化) 一个接近硬件的语言是不够的。视觉计算任务对于高分辨率和实时性能的需求,通常意味着只有通过复杂且有挑战性的性能工程 (performance engineering),才能使得一个能工作的系统达到理想的性能。
一个高度优化的视觉计算程序常常可以比一个实现合理的C /CUDA程序快一个数量级。比如说,一个移动最小二乘物质点法 (Moving Least Squares Material Point Method, MLS-MPM, SIGGRAPH 2018) 中的 粒子到网格 (Particle-to-grid, P2G) 程序,经过合理的C 性能工程,在同样的硬件上能够达到一个朴素实现的 7 倍的性能。其中,性能提升来自使用SSE的 4-wide向量化、用软件实现能够在 L-1 data cache 缓存全局稀疏数据结构上局部节点的数据的机制、循环展开等。
生产力、性能、可移植性取舍
宏观上,传统编程语言往往面临生产力和性能的取舍。类似C 和CUDA的语言往往自身就有一定学习难度。其中高级特性,如模板元编程 (Template meta-programming),虽然合理使用后能够实现代码到计算硬件的直接映射 (direct mapping to hardware) 与零开销抽象 (zero-overhead abstraction) 以达到高性能,但是其学习难度往往让新手望而生畏。另外,要开发高性能视觉计算系统,开发者需要对于C 编译过程、乃至计算机体系结构的深入理解。用一句话总结,一个基本的C 程序本身通常不会为视觉计算等任务带来高性能,而向量化、循环展开、加速数据结构、内存排布优化、数据压缩等性能优化技巧,会让代码非常难以阅读、维护和调试。
另一方面,传统编程语言通常缺乏可移植性,并且常常将软件实现与硬件捆绑。最简单的例子是C 和CUDA会限制程序仅能在CPU或NVIDIA GPU上运行。这对于视觉计算任务往往是难以接受的,因为视觉计算可以在任何地方发生,从配备高端GPU的工作站到没有GPU的移动设备 - 开发者可能会希望在没有GPU的时候程序能够自动选择CPU运行。我们需要一个可移植的编程系统使得同样一段代码能够在不同平台运行。
新计算模式
除去生产力、性能、可移植性这些已经存在了几十年的老问题,最近还有新的视觉计算模式 (pattern)使得传统语言难以胜任相关开发。比如:
3D视觉数据通常具有空间稀疏性 (spatial sparsity),他们需要稀疏数据结构以达到高性能。但是,用传统语言 数据结构库的方式编写稀疏计算程序是困难的。
深度学习方法的崛起使得研究人员对于可微分 (differentiable) 的视觉计算任务非常感兴趣,但是C 一类的语言缺乏对于可微编程 (differentiable programming)的原生支持。
使用低精度 (low-precision)、量化 (quantized) 的数据类型来节约内存空间与带宽逐渐变得流行,因为 1) GPGPU大行其道,但GPU内存有限 2) 现代处理器FLOPS富余而内存带宽紧缺,并且这两种计算资源的差距正继续拉大。为了同时达到节约内存和高性能,在命令式编程语言中实现对低精度数据格式的编译器级别的支持是必要的。
太极(Taichi)编程语言及其设计目标
为了解决之前提到的问题,我们设计了Taichi编程语言。Taichi在设计之初就注重通过提供领域特定语言抽象 (domain-specific language abstractions) 与编译优化 (compiler optimizations) 来同时达到高生产力和高性能,并满足新兴视觉计算模式的需求。其可移植性通过多后端 (multiple backends) 的设计来达到,包括 x64, ARM, CUDA, Metal, OpenGL compute shader等。
范围(scope)
Taichi主要面向以并行for循环 命令式编程为主体的计算任务。利用多核处理器进行并行编程已成为高性能计算的主旋律,而命令式(Imperative,区别于函数式functional)的编程范式更为贴近处理器和高性能程序员的编程习惯。许多视觉计算任务的计算模式也属于这个范围。比如在图像处理、计算物理等任务中,常常需要以并行的方式遍历所有的像素(或粒子、网格、节点等),在 Taichi 中这些模式均可以表达为一个并行for循环。(注:类似的描述包括SIMD/SPMD/SIMT,如OpenMP parallel for、CUDA kernel等,本文不再对其中细微差异进行区分。)
尽管Taichi尽力覆盖广泛的视觉计算任务,某些计算在Taichi的应用范围之外:
具有领域特性硬件 (domain-specific hardware) 的任务;
粒度 (granularity) 足够粗以至于函数调用、数据传输的开销可忽略,并且有高度优化的库解决方案的任务。
具体来说
传统渲染任务往往有着光栅化、光线追踪硬件的支持。实时图形API,如OpenGL、DirectX、Metal、Vulkan往往已经足够适用。
视频编码、解码任务,常常有硬件编码、解码器的支持。
使用标准层 (如卷积、Batch normalization等) 的深度神经网络,常常已经被深度学习框架,如TensorFlow/PyTorch较好地解决。
尽管Taichi并非为以上应用设计,我们确实需要考虑Taichi与以上系统的高效交互。良好设计的零拷贝 (zero-copy API) 通常是好的选择。比如,让OpenGL (用于渲染)的顶点着色器(vertex shader)直接读取Taichi生成的compute shader(用于模拟)的粒子位置数据是非常有帮助的。另一个例子是Taichi的PyTorch界面允许(可微)的Taichi程序深度嵌入PyTorch,使其能够直接用于深度神经网络的训练、推理之中。
设计目标
Taichi有两个高层设计目标:
- 简化高性能视觉计算系统的开发与部署
- 探索新的视觉计算编程语言抽象与编译技术
在本文下一节中,我们会简要论述Taichi面向第一个目标的做出的工程实践。本系列后续文章的主体讨论会集中于第二的目标,即Taichi系列工作的科研贡献。
实用主义(Pragmatic)的设计决策
本节中我们总结Taichi系统的工程实践。工程方面,Taichi的最高设计原则是使得Taichi更容易地被开发者使用,这是高度实用主义的。我们在工程细节上投入了大量精力,来提高Taichi的Python前端 (frontend) 易用性和跨平台兼容性。
Taichi的编译流程如下图所示。
Taichi kernel的一生。Python AST被逐步下沉 (progressively lowered) 为在CPU和GPU上可执行的高性能程序。一系列领域特定的变换与优化保证运行时性能。
几个关键设计决策如下:
1 命令式 (imperative)。图形程序,特别是物理仿真,通常有较高的性能诉求。这使得和函数式(functional)编程相比更接近硬件的命令式编程更为合适。由于更贴近硬件,命令式编程更容易榨取并行处理器的性能。另一个使用使用命令式编程的动机是提高与已有图形算法的兼容性:Taichi提供常用的控制流语句 (if, for, while, continue, break等),并且所有的数据存储 (ti.field) 是可以直接原地部分更改的 (in-place partial modification)。
2 可编程宏内核 (programmable megakernels)。Taichi使用宏内核 (megakernels) 编程模式,允许开发者自然地(有时甚至是激进地)聚合 (fuse) 计算的多个阶段进入一个单一内核 (kernel)。与使用逐元素运算符(如TensorFlow/PyTorch中的"Op"/"Operator")组装复杂运算相比,Taichi kernels有更高的算术强度 (arithmetic intensity,既每byte数据上发生的FLOPs),在视觉计算任务中往往能够达到更高性能。
3 Python嵌入 (Embed in Python)。Python极其易于学习并且被广泛采用。Taichi的前端语法是Python的子集,这使得任何一个Python程序员都能够轻易地学习、使用Taichi。我们使用Python AST灵活的自省 (inspection) 机制来把Python的AST转化为Taichi的AST,随后进入我们自己的编译和运行时系统。将Taichi的前端嵌入进Python有如下好处:
- 容易运行。嵌入在解释性的Python语言而不是编译性的语言(如C )中,大大方便了Taichi程序的运行,因为母体语言的预先编译 (ahead-of-time compilation) 不再需要了。
- 容易重用已有的Python基础设施并与其交互,包括IDE (PyCharm等)、包管理器 (pip)、已有的Python包(如matplotlib、numpy、torch等)。
4 即时 (Just-in-time, JIT) 编译。JIT不但提供了极强的编程灵活性,还延迟了”编译期常量“的需求。比如,在物理模拟器中,时间步长 ∆t 通常被实现成运行时变量,而使用JIT的时候则可以被处理成编译期常量。这允许编译器进行更多的优化,如常量折叠 (constant folding)。同时,Taichi支持模板元编程,伴随着JIT的懒惰编译技术大量节省了不必要的编译时间。另外,对于无法运行Python的环境,如移动端设备,我们也提供提前编译 (Ahead of time, AOT) 相关设施。
5 面向数据的设计 (data-oriented design)。视觉计算任务常被内存带宽所限制。我们采取了面向数据的设计(而不是传统的面向对象的设计)。这使得我们能够更好的优化缓存利用率 (cacheline utilization) 和命中率 (cache hit rate)。更进一步,Taichi实现了数据排布与算法的解耦 (decoupling)。我们会在后续文章描述更多相关细节。
在以上设计决策的帮助下,Taichi在视觉计算开发者和研究者中变得流行。他们通常有使用Python编写高性能GPU程序的诉求。截至2021年4月,Taichi已经被下载50万余次,在GitHub拥有13.1K个star。Taichi被分布在世界各地的超过60个开发者共同开发。
一个简单的Taichi程序如下所示。关于更多Taichi语法的内容,请见我们在SIGGRAPH 2020的Taichi教程。关于Taichi编译器内部实现的更多系列,一个概览在”Life of a Taichi Kernel“中。
一个简单的Taichi程序(左)生成分形动画(右)。Taichi镶嵌在Python之中并极易学习,特别是对已经有Python使用经验的用户。我们的编译器和运行时系统有效地将计算密集的代码(如上图的paint kernel)指派到并行处理器,如GPU,上运行。
下回预告
关于太极的设计动机与工程实践在此告一段落。接下来我们用一系列文章,介绍Taichi在SIGGRAPH、ICLR等图形学与人工智能会议上发表的一系列科研工作。这些工作为Taichi引入了新的语言抽象以及针对性的编译器优化,使得开发者在对应的计算任务上能够事半功倍。
联系我们
文章作者
胡渊鸣 (yuanming@taichi.graphics
)
匡冶 (yekuang@taichi.graphics
)
简历投递 (hr@taichi.graphics
)