OCaml 多核 - 2020年7月
欢迎阅读 2020 年 7 月的多核 OCaml 报告!此更新以及之前的更新由 @shakthimaan、@kayceesrk 和我共同整理。在 OCaml 上游以及我们的多核代码树中都取得了许多进展。
多核 OCaml
通过域执行上下文实现线程兼容性
简而言之:一旦合并了#381,dune 将能够与多核 OCaml 协同工作。
正如我上个月提到的,缺少一个与传统 OCaml 的 Thread 模块向后兼容的模块,是阻碍生态系统兼容性的一个主要障碍。乍一看,这可能有点令人困惑——为什么多核 OCaml 需要非并行线程支持?答案在于多核 OCaml 中并发和并行之间的关系。并发是指我们如何划分多个计算,使它们在重叠的时间段内运行,而并行是指我们如何同时在不同的内核上运行它们以获得更高的性能。许多包(最值得注意的是 Dune)目前使用 Thread 模块在编写直线代码时方便地获得并发性,而无需使用单子抽象。这些用法不需要并行性,但很难重写以避免使用基于线程的并发。
因此,多核 OCaml 也需要一种方法来提供一个性能合理的 Thread 版本。我们尝试的第一个解决方案(由 @jhw 启动,由 @engil 在#342中继续)将一个 Thread 映射到一个多核域,但对于大量线程来说扩展性不佳,因为我们可能拥有比可用 CPU(域实例)数量多得多的并发上下文(Thread 实例)。这导致了一些头脑风暴(#357),以找出适用于像 Dune 或XenServer 堆栈这样的重度 Thread 用户的应用程序的解决方案。
我们的解决方案引入了我们称为#381 中的域执行上下文的概念,它允许我们将多个系统线程映射到 OCaml 域。一旦这个 PR 被审查并合并到多核 OCaml 分支中,它将解锁更多生态系统包,因为 Dune 构建系统将能够在未修改的情况下进行编译。此后,更广泛的 opam 测试的最后一个“主要”剩余障碍是 ocaml-migrate-parsetree,它需要一个小的补丁来支持多核 OCaml 代码树中存在的 effect
关键字语法。
域本地存储
域本地存储 (DLS) (#372) 是一种将 OCaml 值私密地附加到域的简单方法。在LU 分解基准测试的 PR 中展示了一个使用 DLS 时加速的良好示例。在这种情况下,基准测试需要大量随机数,并在本地并行初始化到域中是一个胜利。
另一个示例是进化算法的并行实现(最初由 @per_kristian_lehre 在#336中提出),它在#151中得到了很好的加速(对于那些想要查看基线的人,在#155中有一个顺序版本,您可以在 Sandmark 网页界面中查找)。
使用多核 OCaml 进行并行编程(文档)
关于使用多核 OCaml 进行并行编程的教程现已推出。它提供了对多核 OCaml 的介绍,并解释了 Domains
、Domainslib
和 Channels
的概念。还使用示例说明了如何使用 perf
和 Eventlog
对 OCaml 代码进行分析。
这份草稿已在Reddit和HackerNews上分享,因此您可以在那里找到更多关于它的讨论。
Coq 基准测试
OCaml 的 Sandmark 基准测试套件已成功更新为使用 dune.2.6.0 并为多核 OCaml 4.10.0 构建。通过此主要升级,我们还能够包含 Coq 及其依赖项。我们正在努力向测试套件中添加更多 Coq 回归基准测试。
上游 OCaml
上游 OCaml 代码树在 4.12.0dev 代码树中出现了大量活动,其中包含了为多核 OCaml 做准备的更改。最大的一项是(引用 @xavierleroy 的话)ocaml/ocaml#9728 中的传说中的无页压缩器。这是继上个月的工作(#9698)之后,消除了在编译器使用“no-naked-pointers”选项构建时使用页表的做法,并为将并行多核 OCaml 运行时集成到未来版本的 OCaml 中铺平了道路。
我们希望纳入 OCaml 4.12 的另一个更改是,在标记和扫描时统一垃圾收集器颜色的使用。 #9756 更改使上游运行时使用我们在将并行性改造到 OCaml 上 的 ICFP 论文中描述的相同方案,并进行了一些额外的改进,您可以在 PR 审查评论中阅读。
如果您对完整的更改集感兴趣,您可以查看迄今为止在上游关闭的所有多核先决条件问题。
详细更新
与之前的更新一样,首先列出多核 OCaml 更新,然后是 Sandmark 基准测试项目的增强功能。最后提及上游 OCaml 的正在进行和已完成的更新,以供参考。
多核 OCaml
正在进行
-
ocaml-multicore/ocaml-multicore#342 使用域实现线程库
这是对 @jhwoodyatt 为域实现的 Thread 库进行重新基线化的一项持续工作。
-
ocaml-multicore/ocaml-multicore#357 使用 pthreads 实现 systhreads
在此实现中引入了域执行上下文 (DEC) 作为一种并发抽象,用于使用 pthreads 实现 systhreads。
-
ocaml-multicore/ocaml-multicore#374 在次要收集时强制执行主要切片
域中被阻塞的线程在通过
handle_interrupt
为次要收集器提供服务时可能无法推进主要 GC,因此我们需要进行次要收集来调度主要收集切片。
已完成
域本地状态
-
Sudha247/ocaml-multicore#1
dls_root
应在终端 GC 之前删除全局根的删除会在标记堆栈上推送一个对象,因此在终端 GC 之前需要执行最终 GC。
-
ocaml-multicore/ocaml-multicore#372 域本地存储
RFC 提案ocaml-multicore#339 实现域本地存储已完成并合并到多核 OCaml 中。
删除并发次要 GC 中的残余部分
-
ocaml-multicore/ocaml-multicore#370 删除 Cloadmut 和 lloadmut
此补丁已清理了
Cloadmut
和Iloadmut
的实现和用法。这简化了代码并使其更接近于标准 OCaml。 -
ocaml-multicore/ocaml-multicore#371 域中断清理
在
runtime/domain.c
中,已删除struct interruptor* sender
。域 RPC 函数已在domain.h
中组合在一起,并已应用一致的定义命名。
代码清理
-
ocaml-multicore/ocaml-multicore#367 删除一些未使用的 RPC 消费者
域 RPC 机制不再使用,已被删除。
-
ocaml-multicore/ocaml-multicore#368 删除 read_barrier 和 caml_promote 的死代码部分
此 PR 删除了
caml_promote
、ARM 和 AMD 上读取故障的汇编以及读取故障的全局变量。
其他
-
ocaml-multicore/ocaml-multicore#366 添加事件以记录空闲域
已添加
domain/idle_wait
和domain/send_interrupt
事件来跟踪处于空闲状态的域。下面显示了一个带有此效果的 eventlog 屏幕截图
-
ocaml-multicore/ocaml-multicore#369 将 caml_urge_major_slice 分割成 caml_request_minor_gc 和 caml_request_major_slice
caml_urge_major_slices
被拆分为caml_request_minor_gc
和caml_request_major_slice
。这减少了次要垃圾回收的总次数,如下所示。
-
ocaml-multicore/ocaml-multicore#373 修复了当当前目录名称包含空格时 opam pin 命令。
使用
opam pin
的-k path
命令行参数来处理包含空格的目录名称。 -
ocaml-multicore/ocaml-multicore#375 仅在需要时锁定全局空闲列表以采用池。
当没有全局池需要采用时,分配时的锁获取和释放被移除。
-
ocaml-multicore/ocaml-multicore#377 将 Travis CI 中运行的环境变量分组。
OCAMLRUNPARAM
参数定义为使用USE_RUNTIME=d
命令的环境变量的一部分。 -
ocaml/dune#3608 上游多核 Dune 引导补丁
此补丁用于使用辅助编译器方法构建 Dune,以解决 ocaml/dune#3548 中的问题。
基准测试
正在进行
-
ocaml-bench/sandmark#107 添加 Coq 基准测试
Sandmark 已升级到使用 dune.2.6.0 以及多核 OCaml 4.10.0,这使得我们能够安装 Coq 及其依赖项。我们目前正在努力向 Sandmark 添加更多 Coq 回归基准测试。
-
ocaml-bench/sandmark#122 代码大小的测量
基准测试的代码大小是
flambda
分支所需的一个测量指标,我们正在探索将其添加到 Sandmark 基准测试运行中。 -
ocaml-bench/sandmark#142 [RFC] 用户如何配置 Sandmark 运行?
我们正在收集用户关于如何配置 Sandmark 基准测试的反馈和建议。请在本次讨论中分享您的想法和意见。
-
ocaml-bench/sandmark#150 可用的 Coq 文件
在 Sandmark 中添加更多用于基准测试的 Coq 文件。
已完成
Dune 2.6.0 升级
-
ocaml-bench/sandmark#131 更新解压缩基准测试
@dinosaure 更新了解压缩基准测试,使用最新的 decompress.1.1.0 以及 dune.2.6.0。
-
ocaml-bench/sandmark#132 更新依赖包以使用 dune.2.6.0 和多核 OCaml 4.10.0
Sandmark 现已更新为使用 dune.2.6.0 和多核 OCaml 4.10.0,并升级了 30 多个依赖包。您可以使用以下命令进行测试:
$ opam install dune.2.6.0 $ make ocaml-versions/4.10.0+multicore.bench
Coq 基准测试
-
ocaml-bench/sandmark#140 使用 Sandmark 编译 coqc
Coq 编译器已作为依赖包添加到 Sandmark 中,现在允许我们构建和运行 Coq 基准测试。
-
ocaml-bench/sandmark#143 添加了 Coq 库 fraplib 和一个依赖它的基准测试
Formal Reasoning About Programs 书籍的
fraplib
库基准测试现已包含在 Sandmark 中。 -
ocaml-bench/sandmark#144 将 frap 添加为 Coq 基准测试
CompilerCorrectness.v
Coq 文件已作为 Sandmark 中 Coq 的测试基准测试添加。
持续集成
-
ocaml-bench/sandmark#136 在 .drone.yml 中使用 BUILD_ONLY
.drone.yml 文件已更新为使用 BUILD_ONLY 环境变量,仅安装依赖项而不执行 CI 的基准测试。
-
ocaml-bench/sandmark#147 添加支持将标签与基准测试关联
已引入
macro_bench
和run_in_ci
标签与基准测试关联。标记为run_in_ci
的基准测试将在 Sandmark CI 的一部分中执行。
其他
-
ocaml-bench/sandmark#124 在 Makefile 中添加用户可配置的 paramwrapper
现在可以将
--cpu-list
指定为PARAMWRAPPER
环境变量以运行并行基准测试。 -
ocaml-bench/sandmark#134 在 README 中包含更多信息
README 已更新,包含文档以反映 Sandmark 的最新更改。
-
ocaml-bench/sandmark#141 使用其他选项丰富变体
ocaml-versions/*
文件现在使用 JSON 文件格式,允许您指定 OCaml 基本编译器的源 URL、configure
选项和OCAMLRUNPARAMS
。下面提供了一个示例。{ "url" : "https://github.com/ocaml-multicore/ocaml-multicore/archive/parallel_minor_gc.tar.gz", "configure" : "-q", "runparams" : "v=0x400" }
-
ocaml-bench/sandmark#146 将主干从 4.11.0 更新到 4.12.0
Sandmark 现在使用最新的 OCaml 4.12.0 作为 ocaml-versions/ 中的主干。
-
ocaml-bench/sandmark#148 安装 python3-pip 和 intervaltree 以获得干净的 CI 构建
.drone.yml 文件已更新为安装
python3-pip
和intervaltree
软件包,以避免在调用 Makefile 时出现错误。
OCaml
正在进行
-
ocaml/ocaml#9722 基于 EINTR 的信号,再次
此补丁提供了一种新的实现来解决锁定和信号处理问题。
-
ocaml/ocaml#9756 垃圾回收器颜色更改
此 PR 移除垃圾回收器 (GC) 颜色方案中的灰色,以便与多核 OCaml 主要收集器一起使用。
已完成
-
ocaml/dune#3576 在 OCaml 4.12.0 中,空归档不再生成 .a 文件
对于空库,永远不会生成原生归档文件,这修复了在处理空归档文件时与 OCaml 4.12.0 的兼容性。
-
ocaml/ocaml#9541 为检测运行时添加手册页
manual/manual/cmds/instrumented-runtime.etex
文档已根据审查意见更新,并已合并到 OCaml 主干中。 -
ocaml/ocaml#9728 简化的压缩,无需页面表
使用自描述闭包表示来简化压缩器,并摆脱页面表。
我们要感谢社区中所有 OCaml 开发人员和用户持续的支持、代码审查、文档和对多核 OCaml 项目的贡献。
缩略语
- CI:持续集成
- DEC:域执行上下文
- GC:垃圾回收器
- OPAM:OCaml 包管理器
- PR:拉取请求
- RFC:征求意见
- RPC:远程过程调用