OCaml 多核 - 2021 年 5 月

欢迎来到 2021 年 5 月 多核 OCaml 月度报告!本月的更新以及 之前的更新 由 @avsm、@ctk21、@kayceesrk 和 @shakthimaan 编写。

首先,我们在 OCaml 编译器上的所有上游活动现在都作为闪亮的新 编译器开发时事通讯 #2 的一部分报告,该时事通讯由 @gasche 启动。这代表了一个小小的但重要的转变——仅域多核已牢牢锁定了 OCaml 5.0 的上游路线图,整个 OCaml 编译器团队一直在帮助和贡献它,其中 GC 安全点 特性是最后的几个主要多核先决条件之一(并且即将在 OCaml 4.13 中实现)。

这份多核时事通讯现在将重点关注如何使我们的生态系统为 OCaml 5.0 中的仅域多核做好准备,以及(非官方的)效果系统和多核 IO 堆栈的进展情况。本月的内容很多,所以请带上您最喜欢的饮料,让我们开始吧 :-)

OCaml 多核:4.12.0+domains

多核编译器现在支持其垃圾回收器的 CTF 运行时跟踪,并且有 工具可以显示垃圾回收器事件的 Chrome 跟踪可视化。Sandmark 中的现有基准测试进行了一些性能改进(稍后将看到加速图),这些改进突出显示了最大程度地利用多核的一些方法。在 domainslib 中,还有一些工作是使用 工作窃取双端队列 将基于任务的并行性扩展到 128 个内核/域,这使我们更接近 Cilk 风格的任务并行性能。

与新功能一样重要的是我们决定做的事情。我们一直在研究和评估域本地分配缓冲区 (DLAB) 一段时间,目的是降低次要 GC 的成本。我们发现最终的性能没有达到我们的预期(更改的复杂性相比),因此我们决定不为 OCaml 5.0 推出此功能。您可以找到 DLAB 摘要 页面总结了我们的经验。当移动部件较少时,我们将回到 OCaml 5.0 之后。

生态系统变化以准备 5.0.0 仅域

随着我们在未来几个月内将 5.0 分支与多核分支合并,我们正在加紧准备工作,以确保 OCaml 生态系统做好准备。

在 opam-repo 中默认提供多核编译器

在接下来的几周内,我们将从 ocaml-multicore/multicore-opam 中的 opam 远程仓库合并多核 4.12.0+domains 及其相关包到 mainline opam 仓库。这样做是为了更方便地使用变体编译器来开始使用 Domain 测试您自己的包。

作为此更改的一部分,将在 opam 仓库中提供两个新的基本包

  • base-domains:此包表示当前编译器具有 Domain 模块。
  • base-effects:此包表示当前编译器具有实验性效果系统。

通过添加对这些包的依赖项,唯一有效的解决方案将是 4.12.0+domains(直到 OCaml 5.0,该版本将具有此模块)或 4.12.0+effects

这样做的目的是让社区包更容易发布使用仅域并行性的代码版本,以领先于 OCaml 5.0,以便我们尽早开始迁移和线程安全。我们目前不鼓励任何人依赖 base-effects,因为它仍然是一个不断变化的目标。

此 opam 仓库更改尚未实现,但我会在合并后在此帖子中发表评论。

调整 Stdlib 以实现线程安全

在移植第三方库之前,我们要做的第一件事就是让 Stdlib 准备好进行线程安全。乍一看,这并不像看起来那样简单:如果我们采用简单地在每个全局状态位周围放置一个互斥锁的幼稚方法,我们的顺序性能会变慢。因此,我们正在执行更细粒度的分析和修复,可以在 多核 stdlib 页面 上看到。

对于任何希望贡献的人:在 Stdlib 中搜索全局状态,并将其分类,然后创建一个使用多个运行的域来测试该模块的测试用例,并向 ocaml-multicore 提交 PR。一般来说,如果您现在看到任何构建失败或运行时失败,我们非常感谢您在那里提交问题。您可以看到一些此类问题的良好示例 这里(针对 mirage-crypto)和 这里(针对 Coqt)。

将第三方库移植到域

正如我上个月提到的,我们呼吁希望移植其代码的库和维护者。本月,我们从以下库和应用程序开始

  • Lwt:著名的轻量级线程库现在有一个 PR 来添加 Lwt_domains。这是在 Lwt 中使用多核内核的第一步(简单):它允许您通过 detach : ('a -> 'b) -> 'a -> 'b Lwt.t 在另一个域中运行纯(非 Lwt)函数。

  • Mirage-Crypto:我们正在调整的下一个库是加密库,因为它也是容易并行的低悬果(因为加密函数没有太多全局状态)。端口仍在进行中,因为有一些小规模的构建失败,还有 Format 中的一些 Stdlib 函数尚未实现线程安全,这些函数 导致了失败

  • Tezos-Node:我们正在应用一些先前依赖项的较大应用程序是 Tezos-Node,它通过 Lwt、mirage-crypto、Irmin、Cohttp 和许多其他库使用此处的依赖链。我们现在已经 在 4.12.0+domains 下编译 并且大部分通过了测试套件,但只有在依赖项和 Stdlib 通过测试后才会报告重大结果。

  • Owl:OCaml 最喜欢的机器学习库在 4.12.0+domains 上开箱即用,效果出奇地好。使用它编写的重大机器学习代码库的实验在出现一些错误共享瓶颈之前看到了大约 2-4 倍的加速。考虑到我们没有对代码库本身进行任何更改,这已经很不错了,但请继续关注未来几个月,我们将分析瓶颈并进行更多改进。

希望这能成为大家开始在自己的应用程序上尝试 4.12.0+domains 的信号,特别是了解将其封装在域中的效果,并识别全局状态。您可以阅读我们关于使用多核 OCaml 进行并行编程的实用 教程

我们正在开发一些工具来帮助查找全局状态,但我们需要共同努力来识别其中的一些情况并开始迁移。最重要的是,我们需要在我们的依赖链中有一些多样性——如果您有使用(例如)Async 或 vanilla Thread 模块的有趣应用程序,并且有一些时间与我们合作,请与我或 @kayceesrk 取得联系。

4.12.0+effects

基于效果的 eio 库 正在顺利整合,接口和设计原理都在存储库的 README 中更新。主要的 IO 后端是 ocaml-uring,我们现在正在准备将其单独发布到 opam 仓库,因为它在 Linux 的顺序运行时上也能很好地工作(只要您有相当新的内核。否则内核会崩溃)。我们还有一个 Grand Central Dispatch 效果后端,它为我们提供了一个完全不同的执行模型来练习我们的效果处理程序抽象。

虽然我们不会在本月公布基于效果的 IO 的性能数据,但您可以通过查看 retro-httpaf-bench 存储库来了解我们正在运行的测试类型,该存储库现在具有基于效果、基于 uring 和基于 select 的各种 web 服务器排列组合。我们已向今年夏天晚些时候举行的 OCaml 研讨会提交了演讲稿,如果被采纳,它将为您提供对我们基于效果的 IO 的深入分析。

与往常一样,我们从多核 OCaml 的持续和已完成的任务开始。然后列出生态系统改进,最后是 Sandmark 基准测试项目的更新。最后,为了您的参考,提到了上游 OCaml 工作。对于那些已经读到此处并且觉得没有什么比黑客攻击多核编程运行时更有趣的人,我们正在英国、法国和印度招聘——请在最后查看招聘信息!

多核 OCaml

正在进行

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

    引入了一个新的 --enable-force-instrumented-runtime 选项,用于在链接器调用中使用检测运行时以获取事件日志。

  • ocaml-multicore/ocaml-multicore#553 启用 flambda 时测试套件失败

    b23a416 上启用 flambda 时,一些测试失败,需要进一步调查。

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

    定义一个 CAML_TRACE_VERSION 来区分多核 OCaml 和 trunk 的运行时。

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

    PR 去掉了 Domain.waitDomain.{spawn/join} 中的临界区的使用。

  • ocaml-multicore/ocaml-multicore#559 改善多核 GC 统计信息

    一个草案 PR,用于在使用 OCAMLRUNPARAM=v=0x400 时包含更多多核 GC 统计信息。

已完成

  • ocaml-multicore/ocaml-multicore#508 域本地分配缓冲区

    目前已放弃 OCaml 多核的域本地分配缓冲区实现。PR 本身有讨论,并且有一个维基页面这里

  • ocaml-multicore/ocaml-multicore#527 将 eventlog 移植到 CTF

    eventlog 实现移植到通用跟踪格式 (CTF) 现已完成。

    有关生成运行时事件的 Chrome 跟踪可视化的介绍,请参阅eventlog-tools。此后处理工具将 CTF 跟踪转换为 Chrome 跟踪格式,允许进行交互式可视化,例如:

OCaml-Multicore-PR-527-Illustration|690x475

生态系统

正在进行

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

    eventlog_pausetimes 工具接收一个 eventlog 文件目录,并计算平均暂停时间、最大暂停时间以及高达 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]
    }
    
  • domainslib#29 使用 CL 双端队列进行任务窃取

    使用任务窃取 Chase Lev 双端队列在域之间调度任务的这项正在进行的工作看起来非常有前景。特别是对于具有 128 个内核的机器。

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

    为 retro-httpaf-bench 添加 Eio 基准测试。这是一项正在进行的工作。

  • ocaml-multicore/eio#26 Grand Central Dispatch 后端

    一个早期草案 PR,它实现了 Eio 的 Grand Central Dispatch (GCD) 后端。

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

    一项正在进行的努力,旨在引入 Lwt_domain,用于使用多核 OCaml 的域在 CPU 内核上执行计算。

已完成

retro-httpaf-bench

retro-httpaf-bench 存储库包含用于运行 HTTP 服务器基准测试的脚本。

eio

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

  • ocaml-multicore/eio#18 添加 fibreslib 库

    promise 库已重命名为 fibreslib,以避免与 opam 中的现有软件包发生命名冲突,并且 API(等待者和效果)已分别拆分为各自的模块。

  • ocaml-multicore/eio#19 更新到最新的 ocaml-uring

    代码和配置文件已更新以使用最新的 ocaml-uring

  • ocaml-multicore/eio#20 添加 Fibreslib.Semaphore

    实现了 Fibreslib.Semaphone 模块,该模块对速率限制很有用,并且基于 OCaml 的 Semaphore.Counting

  • ocaml-multicore/eio#21 添加高级 Eio API

    一个新的 Eio 库,它具有用于源和接收器的接口。README 文档已更新,其中包含动机和用法。

  • ocaml-multicore/eio#22 添加结构化并发开关

    结构化并发的实现,以及用于跟踪和使用模拟进行测试的文档示例。

  • ocaml-multicore/eio#23 将存储库重命名为 eio

    OCaml 的基于效果的并行 IO 存储库现在已从 eioio 重命名为 eio

  • ocaml-multicore/eio#24 将 lib_eioio 重命名为 lib_eunix

    名称已更新以匹配 dune 文件。

  • ocaml-multicore/eio#25 检测死锁

    如果调度程序在主线程继续运行时结束,则现在会引发异常以检测死锁。

  • ocaml-multicore/eio#27 将预期测试转换为 MDX

    预期测试已更新以使用 MDX 格式,这避免了对 ppx 库的需要。

  • ocaml-multicore/eio#28 尽可能使用 splice 复制

    已实现效果 Splice 以及对 ocaml-uring 的更新,以及必要的文档。

  • ocaml-multicore/eio#29 改善开关中的异常处理

    额外的异常检查,用于处理多个线程失败的情况,以及用于 Switch.checkFibre.fork_ignore 的情况。

  • ocaml-multicore/eio#30 添加 eio_main 库以自动选择后端

    使用 eio_main 根据平台选择适当的后端(例如 eunix)。

  • ocaml-multicore/eio#31 添加 Eio.Flow API

    实现了 Flow 模块,该模块允许组合,例如双向流和可关闭流。

  • ocaml-multicore/eio#32 对网络的初步支持

    Eio 为网络提供了一个高级 API,并且已添加了 Network 模块。

  • ocaml-multicore/eio#33 在 README 中添加一些设计原理说明

    README 已更新,其中包含设计说明,以及有关对象能力模型原理的进一步阅读参考资料。

  • ocaml-multicore/eio#34 添加 shutdown、允许关闭监听套接字、添加 cstruct_source

    添加了 cstruct_source、shutdown 方法以及源、接收器和文件描述符类型。

  • ocaml-multicore/eio#35 添加 Switch.on_release 以自动关闭 FD

    我们现在可以将文件描述符等资源附加到开关,并且当开关完成时,这些资源会被释放。

杂项

  • ocaml-multicore/domainslib#23 运行测试:从 run_test 目标中的手动命令移至 dune runtest

    dune runtest 命令现在用于执行测试。

  • ocaml-multicore/domainslib#24 从 Domain.Sync.{notify/wait} 移至 Mutex & Condition

    使用 MutexCondition 的通道实现现已完成。以下图表显示了性能结果

Domainslib-PR-24|465x500

  • ocaml-multicore/multicore-opam#53 添加 base-domains 和 base-effects 软件包

    base-domainsbase-effects opam 文件现已添加到 multicore-opam 中。

  • ocaml-multicore/multicore-opam#54 将所有多核软件包移至唯一版本和 base-domains 依赖项

    命名约定现在是使用 base-effectsbase-domains 作为所有地方的名称。

基准测试

正在进行

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

    一项正在进行的工作,旨在升级 Sandmark 以使用 dune.2.8.1 构建 4.13.0+trunk 并生成基准测试。您可以使用以下方法进行测试

    TAG='"macro_bench"' make run_config_filtered.json
    RUN_CONFIG_JSON=run_config_filtered.json make ocaml-versions/4.13.0+trunk.bench
    

已完成

Sandmark

性能
  • ocaml-bench/sandmark#221 修复解压缩工作迭代

    使用 parallel_for,简化 data_to_compress 以使用 String.init,以及修复正确计算配置和完成的工作量,产生了以下速度提升

PR-221-Time |690x184 PR-221-Speedup |690x184

  • ocaml-bench/sandmark#223 更好的弗洛伊德·沃舍尔算法

    弗洛伊德·沃舍尔算法实现的改进,修复了随机种子以使其可重复,并改进了模式匹配。

Sandmark-PR-223-Time|690x184 Sandmark-PR-223-Speedup|690x184 Sandmark-PR-223-Minor-Collections|690x185

  • ocaml-bench/sandmark#224 一些矩阵乘法的改进

    matrix_multiplicationmatrix_multiplication_multicore 代码已更新以方便维护,并且结果仅在对值求和后写入。

Sandmark-PR-224-Time|690x184 Sandmark-PR-224-Speedup|690x184

  • ocaml-bench/sandmark#225 更好的多核 EA 基准测试

    进化算法现在在 fittest 中插入了一个轮询点,以改善基准测试结果。

Sandmark-PR-225-Time|690x184 Sandmark-PR-225-Speedup|690x184

  • ocaml-bench/sandmark#226 针对 mandelbrot6_multicore 的更好缩放

    现在,mandelbrot6_multicore 随着 parallel_for 的使用而良好扩展,如以下图表所示

Sandmark-PR-226-Time|690x184 Sandmark-PR-226-Speedup|690x184 Sandmark-PR-226-Minor-Collections|690x184

  • ocaml-bench/sandmark#227 使用高核心数量改进 nbody_multicore 基准测试

    现在 energy 函数针对更大的核心数量使用 parallel_for_reduce 并行化了。

Sandmark-PR-227-Time|690x184 Sandmark-PR-227-Speedup|690x184

  • ocaml-bench/sandmark#229 改进 game_of_life 基准测试

    现在将热函数内联以改进 game_of_life 基准测试,并且我们避免使用随机数初始化临时矩阵。

Sandmark-PR-229-Time|690x184 Sandmark-PR-229-Speedup|690x184

杂项
  • ocaml-bench/sandmark#215 从 treiber_stack.ml 中删除 Gc.promote_to

    4.12+domains 和 4.12+domains+effects 分支已从运行时中删除了 Gc.promote_to

  • ocaml-bench/sandmark#216 添加 4.12.0+stock、4.12.0+domains、4.12.0+domains+effects 的配置

    现在已将 4.12.0+stock、4.12.0+domains 和 4.12.0+domains+effects 的 ocaml-version 配置文件包含到 Sandmark 中。

  • ocaml-bench/sandmark#220 尝试改进 OCAMLRUNPARAM 文档

    README 已更新,其中包含有关运行基准测试时使用 OCAMLRUNPARAM 配置的更多文档。

  • ocaml-bench/sandmark#222 弃用 4.06.1 和 4.10.0 并升级到 4.12.0

    已删除 4.06.1、4.10.0 ocaml-version,并且 CI 已更新以使用 4.12.0 作为默认版本。

current-bench

  • ocurrent/current-bench#103 能够在 UI 上设置比例以从 0 开始

    现在,图表原点从 [0, y_max+delta](对于 y 轴)开始,以便于比较。

    current-bench frontend fix 0 baseline

  • ocurrent/current-bench#121 使用字符串表示形式表示 Docker CPU 设置。

    OCAML_BENCH_DOCKER_CPU 设置现在从整数切换到字符串,以支持并行执行的 CPU 范围。

OCaml

正在进行中

  • ocaml/ocaml#10039 安全点

    已发布 Sandmark 基准测试运行以获取 4.13.0+trunk 的安全点 PR 的性能数据。PR 已准备好合并。

职位招聘

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

缩写

  • AMD:超微半导体
  • API:应用程序编程接口
  • CI:持续集成
  • CPU:中央处理器
  • CTF:通用跟踪格式
  • DLAB:域本地分配缓冲区
  • EA:进化算法
  • GC:垃圾回收器
  • GCD:Grand Central Dispatch
  • HTTP:超文本传输协议
  • OPAM:OCaml 包管理器
  • MVP:最小可行产品
  • PR:拉取请求
  • TPS:每秒事务数
  • UI:用户界面