OCaml 多核 - 2021年6月

欢迎阅读 2021 年 6 月的 多核 OCaml 月度报告!本月更新以及上个月更新由 @avsm、@ctk21、@kayceesrk 和 @shakthimaan 共同整理。

我们的总体目标仍然按计划进行,将在夏季生成 OCaml 5.0 多核域限定并行处理的预览树。

4.12.0+domains 的生态系统兼容性

5 月份的更新中,我提到我们的重点现在是调整生态系统以更好地与多核协同工作,我很高兴地报告说,这方面进展非常顺利。

  • 4.12.0+domains 多核编译器变体已合并到主线 opam-repo中,因此您现在可以直接 opam switch 4.12.0+domainsbase-domains 包也可用于将您的 opam 项目标记为需要Domains 模块,因此您现在甚至可以将您早期支持多核的库发布到主线 opam 存储库。

  • OCaml 标准库已针对多个域的并行使用进行了安全化(wikiissue修复);特别是 FormatRandom 模块。当在多个域中运行现有 OCaml 代码时,这些模块是我们发现的主要不兼容性来源。

  • Domain 模块的接口已精简,去除了 critical_sectionwaitnotify,这使得运行时大大简化。GC C-API 接口现已实现,这意味着 Jane Street 的 BaseCoreAsync 现在可以在 4.12+domains 上编译而无需修改;例如,opam install patdiff4.12+domains 切换上开箱即用!

  • Domainslib 0.3.0 已发布,其中包含多项改进,包括用于任务分配的工作窃取双端队列。通过使用原语和 O(1) 查找,域局部变量读取的性能也得到了提升。关于多核 OCaml 中的并行编程 的章节已更新以反映 Domainslib 的最新进展。

这意味着大型应用程序堆栈现在应该能够很好地使用 4.12.0+domains 进行编译(例如 Tezos 节点和 patdiff 会使用 opam 中许多依赖树)。如果您发现任何不兼容性,请在存储库中报告。

4.12.0+domains+effects

我们的大部分精力都集中在使仅域树(用于 OCaml 5.0)快速运行上,但我们也在推进基于直接风格效果的 IO 堆栈。

  • Linux Io_uring 的 uring 绑定现在可在 opam-repository 上使用,因此您也可以在顺序 OCaml 上试用它。一个不错的迷你项目是为现有的 Async 或 Lwt 引擎添加 uring 后端,如果有人想尝试进行实质性贡献的话。
  • 现在,eio 已经相当可用,可以用于文件系统和网络。我们已向 OCaml 研讨会提交了一个演讲,将更详细地介绍其内部结构,因此,如果被接受,请在接下来的几个月里继续关注。这里的主要变化是性能改进,HTTP 堆栈与(例如)rust-hyper 相当具有竞争力。

我们很快也会有一个此树的变体,它将移除自定义效果语法并将纤维(运行时部分)实现为 Obj 函数。这将进一步提高生态系统兼容性,并允许我们构建内部使用纤维来提供并发性的直接风格 OCaml 库,而无需在它们的接口中公开任何效果的使用。

基准测试和性能

我们始终渴望获得更多能利用多核功能的基准测试;如果您想尝试多核并帮助编写基准测试,可以在wiki上找到一些建议。我们有一个私有服务器,它运行一个带有 Jupyter 笔记本的 Sandmark 每晚基准测试管道,我们可以向提交基准测试的任何人提供访问权限。我们继续测试 Sandmark 与current-bench的集成,以便更好地与 GitHub PR 集成。

与往常一样,首先列出多核 OCaml 的正在进行和已完成的任务,然后是生态系统及其相关库的更新。然后会提到 Sandmark 基准测试和每晚构建工作。最后,为了方便您参考,提供了上游 OCaml 安全点 PR 的状态。

多核 OCaml

正在进行

已完成

增强功能

  • ocaml-multicore/ocaml-multicore#552 添加一个 force_instrumented_runtime 选项到配置

    configure 脚本现在接受一个新的 --enable-force-instrumented-runtime 选项,以方便在链接器调用中使用检测运行时以获取事件日志。

  • ocaml-multicore/ocaml-multicore#558 重构 Domain.{spawn/join} 以不使用临界区

    已移除 Domain.{spawn/join} 中的临界区以及 Domain.wait 的使用。

  • ocaml-multicore/ocaml-multicore#561 简化 Domain.Sync:移除 waitnotifycritical_section

    Domain.Sync 中的一个重大更改,移除了 critical_sectionnotifywaitwait_forwait_until。这是为了消除运行时中域间消息传递的需求。

  • ocaml-multicore/ocaml-multicore#576 在运行时包含 Git 哈希

    现在在运行时打印 Git 哈希,如下所示

    $ ./boot/ocamlrun -version
    The OCaml runtime, version 4.12.0+multicore
    Built with git hash 'ae3fb4bb6' on branch 'runtime_version' with tag '<tag unavailable>'
    
  • ocaml-multicore/ocaml-multicore#579 用于获取 DLS 根的原语

    已实现一个用于获取 DLS 的新原语,它现在是 amd64 上的单个 mov 指令。

上游

  • ocaml-multicore/ocaml-multicore#555 运行时:CAML_TRACE_VERSION 现在设置为多核特定值

    定义了 CAML_TRACE_VERSION 以区分多核 OCaml 和运行时的主干。

  • ocaml-multicore/ocaml-multicore#581 将我们的内联使用移动到 Caml_inline

    我们现在对运行时中的所有 C 内联使用 Caml_inline,以与上游 OCaml 保持一致。

  • ocaml-multicore/ocaml-multicore#589 重新引入 adjust_gc_speed

    主干中的 caml_adjust_gc_speed 函数已重新引入到多核 OCaml 运行时。

  • ocaml-multicore/ocaml-multicore#590 运行时:在 gc_ctrl 中存根 caml_stat_* 接口

    在 gc_ctrl.h 中创建 caml_stat_* 存根函数,以引入一个与 GC stat 实用程序兼容的层,这些实用程序在主干中可用。

修复

  • ocaml-multicore/ocaml-multicore#562 从 DLABs 导入对次要堆分配代码的修复

    用于次要堆分配的乘法因子 2 已移除,并且来自 config.h 的 Minor_heap_max 限制不再转换为多核 OCaml 的字节大小。

  • ocaml-multicore/ocaml-multicore#593 修复与短暂对象相关的两个问题

    一个简化终止期间短暂对象传递的补丁。

  • ocaml-multicore/ocaml-multicore#594 修复终结器传递问题

    使用 caml_finish_major_cycle 导致主要 GC 阶段 Phase_sweep_and_mark_main 正确地传递终结器。

  • ocaml-multicore/ocaml-multicore#596 systhreads:在初始化线程描述符后执行 st_thread_id

    线程 ID 甚至在初始化线程描述符之前就已设置,此 PR 修复了顺序。

  • ocaml-multicore/ocaml-multicore#604 修复 caml_scan_global_young_roots 中未受保护的 caml_skiplist_empty

    此 PR 引入了 caml_iterate_global_roots 函数并修复了全局根的锁定错误。

清理

杂项

  • ocaml-multicore/ocaml-multicore#582 使 Random、Hashtbl 和 Filename 中的全局状态成为域本地状态

    现在 `Random`、`Hashtbl` 和 `Filename` 中的默认状态设置为域本地状态。

  • ocaml-multicore/ocaml-multicore#586 使 Format 中的状态成为域本地状态

    现在 `Format` 中的默认状态设置为域本地状态。

  • ocaml-multicore/ocaml-multicore#595 实现 `caml_alloc_dependent_memory` 和 `caml_free_dependent_memory`

    依赖内存是指依赖于 GC(和终结器)进行释放的堆内存块。`caml_alloc_dependent_memory` 和 `caml_free_dependent_memory` 已添加到 runtime/memory.c 中。

生态系统

正在进行

Domainslib-PR-36-Results|690x383

  • ocaml-multicore/multicore-opam#56 Base-effects 严格依赖于 4.12

    关于在 `base-effects.base/opam` 中使用 OCaml 4.12.0 严格下限的问题。

  • ocsigen/lwt#860 Lwt_domain:一个与多核并行性的接口

    Lwt_domain 模块已移植到 domainslib 任务池,用于使用多核 OCaml 的域在 CPU 内核上执行计算。以下是在配备 24 个隔离内核的英特尔至强金牌 5120 处理器上获得的一些基准测试结果。

    Lwt-PR-860-Speedup|429x371

已完成

Ocaml-Uring

ocaml-uring 存储库包含了 OCaml 的 `io_uring` 绑定。

  • ocaml-multicore/ocaml-uring#21 添加 accept 调用

    accept 调用已添加到 uring,并包含了 `unix` 库作为依赖项。

  • ocaml-multicore/ocaml-uring#22 添加对取消的支持

    添加了一个 `cancel` 方法来请求取消作业。排队操作和测试也已更新。

  • ocaml-multicore/ocaml-uring#24 整理强制转换

    Int_val 已更改为 `Long_val`,以消除在 64 位平台上对符号扩展指令的需求。

  • ocaml-multicore/ocaml-uring#25 修复 test_cancel

    添加了一个带有 `queue_depth` 参数的 `with_uring` 函数来处理取消测试。

  • ocaml-multicore/ocaml-uring#26 添加 `openat2`

    openat2 方法已添加,可访问所有 Linux 打开和解析标志。

  • ocaml-multicore/ocaml-uring#27 微调 C 标记以提高性能

    CFLAGS 已更新以提高性能。对于 noop 基准测试,观察到以下结果。

    Before: noop   10000  │        1174227.1170 ns/run│
    After:  noop   10000  │         920622.5802 ns/run│
    
    
  • ocaml-multicore/ocaml-uring#28 不要允许在正在使用时释放环

    在创建时将环添加到全局集中,并在退出时清理。此外,在分配插槽之前会检查无效的取消请求。

  • ocaml-multicore/ocaml-uring#29 将 iovec 替换为 cstruct 并清理 C 存根

    readv 和 `writev` 现在接受 Cstruct 列表,允许访问 bigarray 的子范围,并与多个缓冲区一起使用。OOM 错误的处理也得到了改进。

  • ocaml-multicore/ocaml-uring#30 修复 API 中剩余的 TODO

    read 和 `write` 方法分别重命名为 `read_fixed` 和 `write_fixed`。`Region.to_cstruct` 已添加作为创建子 bigarray 的替代方法。如果用户请求更大的大小块,则现在会引发异常。

  • ocaml-multicore/ocaml-uring#31 在等待时使用 `caml_enter_blocking_section`

    在等待时使用 `caml_enter_blocking_section` 和 `caml_leave_blocking_section`,这允许其他线程执行,并且在多核 OCaml 的情况下 GC 可以运行。

  • ocaml-multicore/ocaml-uring#32 使用来自 OCaml 的 C 标记编译 uring

    在构建 uring 时使用 OCaml C 标记,并删除未使用的 dune 文件。

  • ocaml-multicore/ocaml-uring#33 准备发布

    CHANGES.md、README.md、dune-project 和 uring.opam 文件已更新以准备发布。

  • ocaml-multicore/ocaml-uring#34 将 `liburing` 转换为子树

    我们现在使用子树而不是子模块,以便可以将 ocaml-uring 提交到 opam 存储库。

多核 OCaml 中的并行编程

Eio

eio 库为多核 OCaml 提供了一个基于效果的并行 IO 堆栈。

新增功能
  • ocaml-multicore/eio#41 添加 eio.mli 文件

    一个包含 `Generic`、`Flow`、`Network` 和 `Stdenv` 模块的 `lib_eio/eio.mli` 文件已添加到存储库中。

  • ocaml-multicore/eio#45 添加基本域管理器

    此 PR 允许您在另一个域上运行 CPU 密集型任务,并向 `traceln` 添加了一个互斥锁以避免输出重叠。

  • ocaml-multicore/eio#46 添加 Eio.Time 并允许取消休眠

    使用 `psq` 而不是 `bheap` 库来允许取消。`Eio.Time` 模块已添加到 `lib_eio/eio.ml` 中。

  • ocaml-multicore/eio#53 添加 `Switch.sub_opt`

    添加了一个新的 `Switch.sub_opt` 实现以允许使用新的开关运行函数。此外,`Switch.sub` 已修改,因此它不是命名参数。

  • ocaml-multicore/eio#54 初始文件系统抽象

    添加了一个 `Dir` 模块以允许文件系统抽象以及创建文件和目录的功能。在 Linux 上,它使用 `openat2` 和 `RESOLVE_BENEATH`。

  • ocaml-multicore/eio#56 添加 `with_open_in`、`with_open_out` 和 `with_open_dir` 辅助函数

    Eio.Dir 模块现在包含 `with_open_in`、`with_open_out` 和 `with_open_dir` 辅助函数。

  • ocaml-multicore/eio#58 添加 `Eio_linux.{readv, writev}`

    Eio_linux.{readv, writev} 函数已添加到 `lib_eio_linux/eio_linux.ml` 中,它使用新的 OCaml-Uring API。

  • ocaml-multicore/eio#59 添加 `Eio_linux.noop` 和一个简单的基准测试

    添加了一个 `Eio_linux.noop` 实现用于基准测试 Uring 调度。

  • ocaml-multicore/eio#61 添加通用 Enter 效果以简化调度程序

    引入了 `Enter` 效果以简化调度程序操作,这对 noop 基准测试没有太大影响,如下所示。

Eio-PR-61-Benchmark|690x387

改进
  • ocaml-multicore/eio#38 将 Flow.write 重命名为 Flow.copy

    代码和文档已更新为将 `Flow.write` 重命名为 `Flow.copy` 以提高清晰度。

  • ocaml-multicore/eio#36 使用 uring 进行 accept

    enqueue_accept 函数现在使用 `Uring.accept` 以及 `effect Accept`。

  • ocaml-multicore/eio#37 性能改进

    优化了 `Eunix.free` 并使用 `Uring.peek` 处理已完成的事件以获得更好的性能结果。

  • ocaml-multicore/eio#48 简化 `Suspend` 操作

    通过用来自 Eio 的代码替换旧的 `Await` 和 `Yield` 效果来简化 `Suspend` 效果。

  • ocaml-multicore/eio#52 将 Linux 支持拆分到 `eio_linux` 库中

    eunix 现在具有不同后端共享的公共代码,而 `eio_linux` 提供了 Linux io-uring 后端。测试和文档已更新以反映此更改。

  • ocaml-multicore/eio#57 使用回溯重新引发异常

    添加了在开关捕获异常时存储回溯引用的支持。当您稍后想要重新引发异常时,这很有用。

  • ocaml-multicore/eio#60 简化完成的处理

    此 PR 在 `type io_job` 中添加了 `Job` 和 `Job_no_cancel`,以及其他 `Log.debug` 消息。

清理
  • ocaml-multicore/eio#42 将 fibreslib 合并到 eio 中

    Fibreslib 代码现在已与 `eio` 合并。您现在需要打开 `Eio.Std` 而不是打开 `Fibreslib`。

  • ocaml-multicore/eio#47 清理网络 API

    网络 API 已更新,其中包含一些更改,例如将 `bind` 重命名为 `listen`,用我们自己的类型替换 `Unix.shutdown_command` 在 Eio API 中,以及用自定义类型替换 `Unix.sockaddr`。

  • ocaml-multicore/eio#49 删除 `Eio.Private.Waiters` 和 `Eio.Private.Switch`

    Eio.Private.Waiters 和 `Eio.Private.Switch` 模块已删除,等待现在使用 Eio 库处理。

  • ocaml-multicore/eio#55 一些 API 和 README 的清理

    此 PR 包含多个清理和文档更改。README.md 已修改为使用Eio.Flow.shutdown代替Eio.Flow.close,并添加了一个时间部分。Eio.Network模块已更改为Eio.NetTime.nowTime.sleep_until方法已添加到lib_eio/eio.ml中。

文档
  • ocaml-multicore/eio#43 添加关于确定性的设计说明

    README.md 文档已更新,并添加了一些关于确定性的设计说明。

  • ocaml-multicore/eio#50 README 改进

    更新了 README.md 并添加了doc/prelude.ml用于 MDX。

处理取消

  • ocaml-multicore/eio#39 允许取消 accept 操作

    此 PR 现在支持取消服务器的 accept 和读取操作。

  • ocaml-multicore/eio#40 支持取消剩余的 Uring 操作

    现在支持取消connectwait_readableawait_writable Uring 操作的请求。

  • ocaml-multicore/eio#44 修复读取取消测试

    ENOENT值已正确修正为使用-2,并且更新了取消读取请求的文档。

  • ocaml-multicore/eio#51 从取消操作中获取EALREADY不是错误

    lib_eunix/eunix.ml中处理EALREADY情况,其中操作在进行中时被取消。

其他

  • ocaml-multicore/eventlog-tools#2 添加一个 pausetimes 工具

    一个名为eventlog_pausetimes的工具已添加到eventlog-tools中,它接收事件日志文件的目录并计算平均值、最大暂停时间以及高达第 99.9 个百分位的分布。例如

    ocaml-eventlog-pausetimes /home/engil/dev/ocaml-multicore/trace3/caml-426094-* name
    {
      "name": "name",
      "mean_latency": 718617,
      "max_latency": 33839379,
      "distr_latency": [191,250,707,16886,55829,105386,249272,552640,1325621,13312993,26227671]
    }
    
  • ocaml-multicore/kcas#9 使用cpu_relax进行回退

    Domain.Sync.{critical_section, wait_for}现在已被Domain.Sync.cpu_relax替换,这与无锁实现相匹配。

  • ocaml-multicore/retro-httpaf-bench#10 添加 Eio 基准测试

    Eio 基准测试现已添加到 retro-httpaf-bench GitHub 存储库中。

  • ocaml-multicore/retro-httpaf-bench#11 在 CI 构建中进行递归检出

    build_image.yml工作流程已更新,以对 CI 构建的子模块执行递归检出。

  • domainslib#29 使用 Chase Lev 双端队列进行任务窃取

    用于跨域调度任务的任务窃取 Chase Lev 双端队列现已合并,并在具有 128 个 CPU 内核的机器上显示出有希望的结果。

  • ocaml-multicore/multicore-opam#55 添加 domainslib 的 0.3.0 版本

    domainslib.0.3.0的 opam 文件已添加到 multicore-opam 存储库中。

基准测试

正在进行

  • ocaml-bench/sandmark-nightly#1 无法更改比较输入值

    parallel_nightly.ipynb笔记本中,下拉选项中的TimestampVariant字段在重新计算整个工作簿时会被重置。

Sandmark-Nightly-1-Issue|690x139

  • ocaml-bench/sandmark#230 使用 dune.2.8.1 构建 4.13.0+trunk 版本

    ocaml-migrate-parsetree.2.2.0ppxlib.0.22.2包现在可用于 4.13.0+trunk,我们目前正在将 Sandmark 中的 Irmin Layers 基准测试从使用 Irmin 2.4 移植到 2.6。

  • ocaml-bench/sandmark#231 在 nightly 笔记本中查看一组基准测试的结果

    使用 Sandmark Jupyter 笔记本时,过滤基准测试列表的功能请求。

  • ocaml-bench/sandmark#233 更新 pausetimes_multicore 以适应最新的 Multicore 更改

    暂停时间现在已针对 4.12.0 上游和 4.12.0 Multicore 分支更新,以使用新的通用跟踪格式 (CTF)。下面说明了顺序和并行暂停时间结果的生成图表

Sandmark-PR-233-Serial-Pausetimes|690x229 Sandmark-PR-233-Parallel-Pausetimes|690x355

  • ocaml-bench/sandmark#235 更新选定的基准测试集作为基线基准测试

    用于比较的基线基准测试应该只来自用户在 Jupyter 笔记本中选择的基准测试。

Sandmark-Issue-235|383x82

  • ocaml-bench/sandmark#236 在 sandmark_nightly 中实现暂停时间支持

    需要在 Sandmark nightly Jupyter 笔记本中实现顺序和并行暂停时间图结果。结果类似于OCaml 并行化改造,ICFP 2020 论文中产生的图 10 和图 12。

  • ocaml-bench/sandmark#237 在更大的机器上运行 sandmark_nightly

    Sandmark nightly 顺序和并行基准测试运行的测试已在 24 核机器上完成,我们希望将其部署在 64 核以上的机器上,以受益于 Domainslib 的最新改进。

  • ocaml-bench/sandmark#241 切换到默认的 Random 模块

    关于是否切换到对顺序 Minilight、全局根微基准测试和进化算法使用Random.State的讨论正在进行中。

已完成

  • ocaml-bench/sandmark#232 num_domains -> num_additional_domains

    基准测试已更新为现在使用num_additional_domains,以与 Domainslib 中的命名保持一致。

  • ocaml-bench/sandmark#239 将 grammatrix 移植到任务池

    Multicore Grammatrix 基准测试现已移植为使用 Domainslib 任务池。时间和加速图如下所示

Sandmark-PR-239-Time|690x357 Sandmark-PR-239-Speedup|690x297

OCaml

正在进行

  • ocaml/ocaml#10039 安全点

    此 PR 目前正在针对 ARM64 和 PowerPC 架构进行测试和评估,特别是应用于Ipoll指令的分支松弛。

感谢社区中所有 OCaml 用户、开发者和贡献者对项目的持续支持。请注意安全!