最近在Github每日热榜上榜的有两个跟并行计算加速相关的开源项目,都是来自Higher Order Company这家公司。
在Github的公司主页上,写着如下介绍:
我们是HOC,一家科技初创公司,目标是构建不可避免的大规模并行计算机未来。我们相信:交互网络(Interaction Net)是一个强大的计算模型,它将催生出大规模并行的运行时和处理器。为了铺平前进的道路,我们开发了如下两个产品。
仓库:HigherOrderCO/HVM
点评: 高阶虚拟机(HVM)是Higher Order Compoany公司出品的,一个 Rust 中的大规模并行优化函数运行时。Higher Order Company官网首页自称其连接着“计算的并行未来”(WELCOME TO THE PARALLEL FUTURE OF COMPUTATION)。这个HVM通过将用高级语言(如 Python 和 Haskell)编写的程序编译为 HVM,人们就可以直接在大规模并行硬件(例如 GPU)上运行这些程序,并获得近乎理想的加速。
1997年,Yves Lafont设计了一种并发计算模型——交互组合器(Interaction Combinators),它在一些基本方面(如并行计算)超越了图灵机和λ-演算。HVM是基于该模型构建出来的一个高级语言编译器和评估器,它具有惰性、非垃圾回收和大规模并行等特性,可自动实现近乎理想的并行加速,最多可支持 1000 多个线程。在某些情况下,对于高阶计算,HVM的性能可能比包括GHC在内的其他方案都要快得多,甚至是指数级的快。
HVM目前是原型,即将推出包含大量优化和正确性工作的生产就绪版本。这种运行方式不仅内存高效(无需垃圾回收),还具有自动并行性和β-最优性。这意味着,你只需编写一个简单的函数程序,HVM就能将其自动转换为大规模并行、β-最优的可执行程序。
HVM通过交互网模型实现,该模型是并发的计算模型,它允许数据在不同地方同时只出现一次,从而减少同步需求。HVM的内存占用与Rust相当,因为它即时收集任何无法到达的数据,没有累积的thunk,不需要全局垃圾回收。然而,HVM目前不实现如引用、循环和局部可变性等内存高效特性,因此其程序的内存占用可能比C和Rust程序大。
仓库:HigherOrderCO/Bend
点评: Bend(弯曲)是一种大规模并行高级编程语言,也是由 HigherOrder 公司出品,由前面介绍的 HVM2 运行时提供支持。
与 CUDA 和 Metal 等低级替代方案不同,Bend 和 Python 以及 Haskell 等具有高度表达力的编程语言有着相同的感觉和功能,包括快速对象分配、完全闭包支持的高阶函数、无限制的递归,甚至更多。同时它可以运行在 GPU 等大规模并行硬件上,具有基于核心数量的近线性加速,以及零显式并行注释:没有线程生成、没有锁、没有互斥、没有原子操作等并行计算中的这些烦人东西,任何可以并行完成的工作都将并行完成。
用官方的说法:使用 Bend,你可以轻松地为多核 CPU/GPU 编写并行代码,而无需成为具有 10 年经验的 C/CUDA 老专家。感觉就像在用Python编程一样!也无需处理并发编程的复杂性:锁、互斥体、原子……
要在 Bend 中编写并行程序,你要做的就是……什么都不做。除了不要把程序写成本质上必须顺序执行的除外!比如写成如下这样的表达式:
((1 + 2) + (3 + 4))
这段表达式天然就无法并行运行,因为 +4
依赖于 +3
,而 +3
又依赖于 (1+2)
。但下面这个表达式:
((1 + 2) + (3 + 4))
却可以并行运行,因为 (1+2) 和 (3+4) 是独立的;根据Bend的基本承诺,它将做到:
Everything that can run in parallel, will run in parallel.
一切能并行运行的东西都会并行运行。
更完整的示例,可以看如下双调排序相关的代码:
(并行双调排序)
# Sorting Network = just rotate trees!
def sort(d, s, tree):
switch d:
case 0:
return tree
case _:
tree =
lft = sort(d-1, 0, x)
rgt = sort(d-1, 1, y)
return rots(d, s, lft, rgt)
# Rotates sub-trees (Blue/Green Box)
def rots(d, s, tree):
switch d:
case 0:
return tree
case _:
tree =
return down(d, s, warp(d-1, s, x, y))
(...)
该文件实现了具有不可变树旋转的双调排序器。它不是您期望在 GPU 上快速运行的算法。然而,由于它使用本质上并行的分治方法,因此 Bend 将以多线程方式运行它。一些基准测试结果如下:
CPU,Apple M3 Max,1 个线程:12.15 秒 CPU,Apple M3 Max,16 线程:0.96 秒 GPU、NVIDIA RTX 4090、16k 线程:0.21 秒
不做任何操作,即可实现 57 倍的加速。没有线程产生,没有锁、互斥体的显式管理。我们只是要求 Bend 在 RTX 上运行我们的程序,它确实运行了。就那么简单。
Bend 不限于特定范例,例如张量或矩阵。任何并发系统,从着色器到类 Erlang 的 actor 模型,都可以在 Bend 上进行模拟。例如,要实时渲染图像,我们可以简单地在每个帧上分配一个不可变的树:
# given a shader, returns a square image
def render(depth, shader):
bend d = 0, i = 0:
when d < depth:
color = (fork(d+1, i*2+0), fork(d+1, i*2+1))
else:
width = depth / 2
color = shader(i % width, i / width)
return color
# given a position, returns a color
# for this demo, it just busy loops
def demo_shader(x, y):
bend i = 0:
when i < 5000:
color = fork(i + 1)
else:
color = 0x000001
return color
# renders a 256x256 image using demo_shader
def main:
return render(16, demo_shader)
它确实会起作用。即使涉及的算法在 Bend 上也能很好地并行。长距离通信通过全局 beta 缩减(根据交互演算)执行,并通过 HVM2 的原子链接器正确有效地同步。
要直接开始操作,请查看 Bend 的 GUIDE.md。
有关详细功能列表,请查看 FEATURES.md。
要了解 Bend 背后的技术,请查看 HVM2 的论文。
官方最后指出:
需要强调的是,虽然 Bend 实现了它的初衷(即通过内核扩展性能,最多可支持 10000+ 个并发线程),但它的单核性能仍然非常低于标准。这是该系统的第一个版本,我们还没有在合适的编译器上投入太多精力。您可以预期每个版本的原始性能都会大幅提高,因为我们致力于正确的代码生成(包括一系列缺失的优化)。同时,您现在可以使用解释器,从 Python 高级语言的角度一睹大规模并行编程的样子!