OCaml 多核 - 2021年9月

欢迎阅读 2021 年 9 月的 Multicore OCaml 月度报告!本月的更新以及 之前的更新 由我 @ctk21、@kayceesrk 和 @shakthimaan 共同整理。团队在过去几个月里一直在努力完成 最后几个功能,以实现与标准 OCaml 的功能奇偶校验。我们还与 OCaml 核心团队密切合作,制定了将 Multicore OCaml 上游到标准 OCaml 的时间线,现在已经达成一致意见:

OCaml 5.0 将通过域支持共享内存并行,以及通过效果处理器支持直接风格并发(无语法支持).

新代码将必须经过对上游 OCaml 贡献的通常严格审查流程,但我们预计将在未来几个月内推进审查流程。

回顾:什么是效果处理器?

以下是摘录自 "将效果处理器改造到 OCaml 上"

效果处理器为用户定义的效果提供了模块化基础。关键思想是将效果操作的定义与其解释分离,解释由效果的处理器给出。例如

effect In_line : in_channel -> string

声明一个名为 In_line 的效果,它以类型为 in_channel 的输入通道作为参数,执行时返回一个 string 值。计算可以在不知道 In_line 效果如何实现的情况下执行 In_line 效果。此计算可能包含在以不同方式处理 In_line 的不同处理器中。例如,In_line 可以通过对输入通道执行阻塞读取或通过将其卸载到 libuv 等事件循环来异步执行读取来实现,而无需更改计算。

由于将效果操作与其实现分离,因此效果处理器能够实现模块化编程的新方法。效果处理器是异常处理器的泛化,其中,除了要处理的效果之外,处理器还提供了执行点的分界延续。此延续可用于稍后恢复挂起的计算。这使得可以组合地表达非局部控制流机制,例如可恢复异常、轻量级线程、协程、生成器和异步 I/O。

OCaml 中的效果处理器的实现是一次性的——也就是说,延续只能恢复一次,如果未使用则必须显式地终止。此限制使在存在可变数据结构的情况下更容易推理控制流,并且还允许实现高性能。

您可以在 完整论文 中阅读更多关于 OCaml 中效果处理器的信息。

为什么 OCaml 5.0 中没有对效果处理器提供语法支持?

Multicore OCaml 中当前的效果处理器无法确保 效果安全性。也就是说,编译器不会确保程序执行的所有效果都被处理。相反,未处理的效果会导致运行时异常。由于我们计划在未来扩展 OCaml 以支持 效果系统,因此 OCaml 5.0 不会提供用于使用效果处理器进行编程的语法支持。相反,我们通过标准库中的函数公开相同的功能,为效果系统落地时保留语法决策。基于函数的效果处理器与 Multicore OCaml 中当前的语法版本一样具有表现力。例如,以下内容的无语法版本:

effect E : string 

let comp () =
  print_string "0 ";
  print_string (perform E);
  print_string "3 "
     
let main () = 
  try 
    comp () 
  with effect E k -> 
    print_string "1 "; 
    continue k "2 ";  
    print_string4 " 

将是

type _ eff += E : string eff
     
let comp () = 
  print_string "0 "; 
  print_string (perform E); 
  print_string "3 "
     
let main () =
  try_with comp () 
  { effc = fun e -> 
      match e with 
      | E -> Some (fun k ->  
          print_string "1 ";
          continue k "2 "; 
          print_string4)
      | e -> None }

可以想象编写一个 ppx 扩展,使程序员能够编写接近早期版本的代码。

我今天应该使用哪个 opam 切换?

4.12+domains opam 切换具有所有将进入 OCaml 5.0 的功能,包括作为函数的效果处理器。这些函数所在的精确模块可能会在 5.0 版中发生更改,但基本形式应该保持不变。

4.12+domains+effects opam 切换将被保留,但语法不会上游。此切换主要用于尝试学术文献中 OCaml 效果处理器的示例。

要了解有关使用此效果系统进行编程的更多信息,请参阅 eio 库和 最近的这次演讲。在接下来的几周内,eio 库将移植到 4.12+domains 以使用基于函数的效果处理器,以便它可以用于 OCaml 5.0。

关于 2021 年 9 月的更新

已合并了许多增强功能,以提高 stdlib 的线程安全性,改进测试套件覆盖率,以及通常的错误修复。生态系统项目的文档已更新,以提高可读性、语法和一致性。Sandmark-nightly 网络服务目前正在 Docker 化,以便部署以可视化和分析基准测试结果。Sandmark 2.0-beta 分支也已发布,其中包含 2.0 功能,可供测试和反馈。

我们要感谢以下人员的贡献

  • @lingmar(Linnea Ingmar)报告了 4.12.0+domains 中 caml_shared_try_alloc 处的段错误。
  • @dhil(Daniel Hillerström)提供了一个补丁来从编译器源代码中删除 drop_continuation
  • @nilsbecker(Nils Becker)报告了在使用 Task.pool 管理时使用 14 个内核发生的崩溃。
  • @cjen1(Chris Jensen)观察并使用 ulimit 修复了在尝试 Eio 自述文件示例时出现的 Unix.ENOMEM 错误。
  • @anuragsoni(Anurag Soni)为 retro-httpaf-bench 贡献了一个异步 HTTP 基准测试。

与往常一样,Multicore OCaml 更新首先列出,然后是生态系统工具和库的更新。最后列出 Sandmark-nightly 的正在进行的工作和 Sandmark 基准测试任务,以供参考。

Multicore OCaml

正在进行

线程安全

段错误

  • ocaml-multicore/ocaml-multicore#639 GC 中的段错误

    正在调查由 @lingmar(Linnea Ingmar)报告的 4.12.0+domains 中 caml_shared_try_alloc 引起的段错误。

  • ocaml-multicore/ocaml-multicore#646 Coq 在构建期间发生段错误

    当使用 Multicore OCaml 运行时,Coq 证明助手会导致段错误,并且已提供了一个新的 tarball 供测试。

测试套件

  • ocaml-multicore/ocaml-multicore#640 Windows 的 GitHub Actions

    GitHub Actions 已更新,可在 Windows 上运行 Multicore OCaml 测试套件。

  • ocaml-multicore/ocaml-multicore#641 使多核测试套件运行程序与标准 OCaml 保持一致

    需要审查 Multicore 禁用的测试,以查看是否可以重新启用它们,以及像 trunk 一样并行运行它们。

杂项

  • ocaml-multicore/ocaml-multicore#637 caml_page_table_lookup 在 ocaml-multicore 中不可用

    ancien 包使用 Is_in_heap_or_young 宏,该宏在内部使用 caml_page_table_lookup,而 caml_page_table_lookup 尚未在 Multicore OCaml 中实现。

  • ocaml-multicore/ocaml-multicore#653 删除 drop_continuation

    由 @dhil(Daniel Hillerström)贡献的 PR,用于删除 drop_continuation,因为 clone_continuation 也已删除。

已完成

上游

  • ocaml-multicore/ocaml-multicore#631 不要从 caml_alloc C 函数中的信号引发异步异常

    一个 PR,防止异步异常从信号处理程序中抛出,并避免从 C 中的 caml_alloc_* 调用轮询挂起的信号。

  • ocaml-multicore/ocaml-multicore#638 为标准库添加一些单射性注解

    为了使用多核 OCaml 编译 stdcompat,单射性注解已从 4.12.0 版本回退到 stdlib

  • ocaml-multicore/ocaml-multicore#642 删除页面表功能的残余部分

    多核 OCaml 中未使用页面表,并且已删除相应的宏和函数定义。

  • ocaml-multicore/ocaml-multicore#643 Core_kernel 字节报告已关闭

    分配的字节数报告出现偏差,因为 young_ptryoung_end 被定义为 char *。更改它们为 value * 的 PR 现已合并。

  • ocaml-multicore/ocaml-multicore#652young_start/end/ptr 指针更改为指向 value

    多核 OCaml 中 young_startyoung_endyoung_ptr 的使用已更新为 value * 而不是 char *,以与主干保持一致。

回退

线程安全

效果处理器

  • ocaml-multicore/ocaml-multicore#650 添加将效果处理器公开为函数所需的原语

    包含促进 4.12+domains 更新的原语,以继续与来自 4.12+domains+effects 的更改一起工作。

  • ocaml-multicore/ocaml-multicore#651 将深层和浅层处理器公开为函数

    该 PR 将深层和浅层处理器作为 Obj 模块中的函数公开。它还删除了克隆延续的功能。

其他

  • ocaml-multicore/ocaml-multicore#633 使用 no-flat-float-arrays 构建 4.12.0+domains 出现错误

    链接器错误已在 PR#644 中修复。

  • ocaml-multicore/ocaml-multicore#647 改进多核的问题模板

    多核 OCaml 错误报告模板已改进,包括“描述问题”、“重现步骤”、“多核 OCaml 构建版本”、“您是否尝试在打开调试运行时和堆验证的情况下运行它?”和“回溯”等部分。

生态系统

正在进行
  • ocaml-multicore/domainslib#43 Task.pool 管理中可能存在错误

    @nilsbecker (Nils Becker) 报告称,在使用 14 个核心时,Task.pool 管理出现段错误。

  • ocaml-multicore/multicore-opam#59 修复 ocaml-multicore/ocaml-multicore#514 之后出现的 batteries 问题

    更新 batteries.3.3.0+multicore opam 文件,为 batteries-included 使用正确的 src URL。

  • ocaml-multicore/multicore-opam#60 多核 domains+effects 语言服务器无法与 VS Code 一起使用

    在使用多核 domains+effects 语言服务器时,VS Code 会显示“Request textDocument/hover failed”错误。

  • ocaml-multicore/eio#81 是否可以实现 IO 优先级?

    关于 IO 优先级和为共识系统调度纤程的查询。

已完成
构建
  • ocaml-multicore/eventlog-tools 使用 ocaml/setup-ocaml@v2

    GitHub 工作流程现已更新为在 .github/workflows/main.yml 文件中使用 4.12.x ocaml 编译器和 ocaml/setup-ocaml@v2

  • ocaml-multicore/tezos#3 添加 cron 作业并运行测试

    CI Dockerfile 和 GitHub 工作流程已更改为定期为 Tezos 在多核 OCaml 上运行测试。

  • ocaml-multicore/tezos#4 每天运行 cron 作业

    GitHub cron 作业现已安排每天从头开始运行 Tezos 构建。

  • ocaml-multicore/retro-httpaf-bench#12 Dockerfile 构建失败

    该问题已不存在,Dockerfile 现在在 CI 中也能正常构建。

  • ocaml-multicore/eio#80 README 示例出现 ENOMEM 错误

    @cjen1 (Chris Jensen) 报告了一个 Unix.ENOMEM 错误,该错误阻止了以下 README 示例代码段的执行。使用 ulimit 设置较小的内存大小可以修复此问题。

    #require "eio_main";;
    open Eio.Std;;
    
    let main ~stdout = Eio.Flow.copy_string "hello World" stdout
    Eio_main.run @@ fun env -> main ~stdout:(Eio.Stdenv.stdout env)
    ;;
    
文档
基准测试
  • ocaml-multicore/retro-httpaf-bench#15 优化 Go 代码

    nethttp-go/httpserv.go Go 基准测试现在使用 Write 而不是 fmt.Fprintf,并删除了 yield() 以进行优化。

  • ocaml-multicore/retro-httpaf-bench 添加异步 HTTP 基准测试

    @anuragsoni (Anurag Soni) 贡献了一个异步 HTTP 基准测试,该测试在具有 4 核 i7-8559 CPU(2.70 GHz)、1000 个连接和 60 秒运行时间的 Docker 中运行。

    retro-httpaf-bench-16-performance|690x460

基准测试

Sandmark-nightly

正在进行

  • ocaml-bench/sandmark-nightly#10 将 sandmark-nightly 容器化

    需要将 sandmark-nightly 服务容器化,以便能够在多台机器上运行。

  • ocaml-bench/sandmark-nightly#11 重构 sandmark-nightly 笔记本

    需要重构和模块化 sandmark-nightly 笔记本中的代码,以便可以将其重用为库。

  • ocaml-bench/sandmark-nightly#12 需要修复归一化图表(包含两个以上基准测试)

    即使存在两个以上基准测试,归一化图表也只生成一个彩色条形图组。与基线相比,它需要显示多个彩色图表。

  • ocaml-bench/sandmark-nightly#13 将每晚运行的日志与结果一起存储

    可以存储每晚运行的日志,因为它们对调试任何故障很有用。

  • ocaml-bench/sandmark-nightly#14 为顺序基准测试添加 best-fit 变体

    sandmark-nightly 运行应包含最佳拟合分配器,因为它优于下一个拟合分配器。可以使用以下命令启用最佳拟合分配器

    $ OCAMLRUNPARAM="a=2" ./a.out
    
  • ocaml-bench/sandmark-nightly#16 最新 Navajo 每晚运行中缺少 Cubicle 和 Coq 基准测试

    顺序基准测试的 UI 由于缺少 Cubicle 和 Coq 基准测试 .bench 文件而无法加载归一化图表。

  • ocaml-bench/sandmark-nightly#17 Navajo 运行使用过时的 Sandmark

    部署在 Navajo 上的 Sandmark 需要更新到最新的 Sandmark,并且由于 Makefile 未提交的更改,git pull 失败。

Sandmark

正在进行

  • ocaml-bench/sandmark#248 Coq 构建失败

    现在可以使用 coq-multicore-2021-09-24 中提供的新 Coq 压缩包进行测试,以使用多核 OCaml 进行构建。

  • Sandmark 2.0-beta

    Sandmark 2.0-beta 分支现已提供测试。它包括新的功能,例如包覆盖选项、向基准测试结果添加元信息、运行多次迭代、基准测试分类、用户配置以及简化包依赖项管理。您可以针对以下 OCaml 编译器变体测试该分支

    • 4.12.0+domains
    • 4.12.0+stock
    • 4.14.0+trunk
    $ git clone https://github.com/ocaml-bench/sandmark.github
    $ cd sandmark
    $ git checkout 2.0-beta
    
    $ make clean; TAG='"run_in_ci"' make run_config_filtered.json
    $ RUN_CONFIG_JSON=run_config_filtered.json make ocaml-versions/4.12.0+domains.bench
    
    $ make clean; TAG='"run_in_ci"' make run_config_filtered.json
    $ RUN_CONFIG_JSON=run_config_filtered.json make ocaml-versions/4.12.0+stock.bench
    
    $ make clean; TAG='"run_in_ci"' make run_config_filtered.json
    $ RUN_CONFIG_JSON=run_config_filtered.json make ocaml-versions/4.14.0+trunk.bench
    
    $ make clean; TAG='"macro_bench"' make multicore_parallel_run_config_filtered.json
    $ RUN_BENCH_TARGET=run_orunchrt BUILD_BENCH_TARGET=multibench_parallel RUN_CONFIG_JSON=multicore_parallel_run_config_filtered.json make ocaml-versions/4.12.0+domains.bench
    

    如果您在我们的 GitHub 项目 页面中遇到任何问题,请报告。

已完成

  • ocaml-bench/sandmark#251 更新依赖项以与 4.14.0+trunk 一起使用

    Sandmark 主分支的依赖项现已更新,以便与 4.14.0+trunk 一起构建。

  • ocaml-bench/sandmark#253 从并行基准测试中删除 Domain.Sync.poll()

    Domain.Sync.poll() 函数调用现已弃用,并且已从 Sandmark 中的并行基准测试中删除。

  • ocaml-bench/sandmark#254 禁用沙盒

    现在在为 Sandmark 构建设置本地 _opam 目录时,默认情况下将 --disable-sandboxing 选项传递给 opam。

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

缩写

  • CI:持续集成
  • CPU:中央处理器
  • GC:垃圾收集器
  • HTTP:超文本传输协议
  • IO:输入/输出
  • OPAM:OCaml 包管理器
  • PR:拉取请求
  • UI:用户界面
  • URL:统一资源定位符
  • VS:Visual Studio