【问题标题】:How does Erlang schedule work for multicore CPU machines?Erlang 调度如何为多核 CPU 机器工作?
【发布时间】:2021-02-03 02:46:41
【问题描述】:

我正在学习 Erlang,并且对并行化工作的轻松程度印象深刻。为了练习一下,我挖出了很好的旧 Fibanocci 序列。在下面的代码中,我尝试通过一次计算三个昂贵的产品来利用并行化。

-module (fib4).
-export ( [main/1] ).

main (N) ->
    fib (list_to_integer (atom_to_list (hd (N) ) ) ),
    halt (0).

path (1, Acc) -> Acc;
path (N, Acc) when N rem 2 =:= 0 ->
    path (N - 1, [step | Acc] );
path (N, Acc) ->
    path ( (N - 1) div 2, [jump | Acc] ).

fib (N) -> fib (1, 1, path (N, [] ) ).

fib (N, Nplus1, [Last] ) ->
    case Last of
        step -> Nplus1;
        jump -> N * N + Nplus1 * Nplus1
    end;

fib (N, Nplus1, [jump | T] ) ->
    Pid = self (),
    spawn (fun () -> Pid ! {n1sq, Nplus1 * Nplus1} end),
    spawn (fun () -> Pid ! {mul, 2 * N * Nplus1} end),
    spawn (fun () -> Pid ! {nsq, N * N} end),
    {Nsq, N1sq, Mul} = loop (0, 0, 0),
    fib (Nsq + N1sq, N1sq + Mul, T);

fib (N, Nplus1, [step | T] ) ->
    fib (Nplus1, N + Nplus1, T).

loop (Nsq, N1sq, Mul) ->
    receive
        {nsq, Val} ->
            if
                N1sq > 0 andalso Mul > 0 -> {Val, N1sq, Mul};
                true -> loop (Val, N1sq, Mul)
            end;
        {n1sq, Val} ->
            if
                Mul > 0 andalso Nsq > 0 -> {Nsq, Val, Mul};
                true -> loop (Nsq, Val, Mul)
            end;
        {mul, Val} ->
            if
                N1sq > 0 andalso Nsq > 0 -> {Nsq, N1sq, Val};
                true -> loop (Nsq, N1sq, Val)
            end
    end.

我在 Phenom X4 上运行此代码,在我的机器上计算 fib(10000000) 的那一刻,只有一到两个内核在工作,其他内核在闲置。

我的问题是:

  • 谁来决定分配工作线程的核心数? Erlang 节点或我的操作系统(在我的情况下是 2.6.38 的 ubuntu)?
  • 是否会因为两个或三个内核处于空闲状态而导致速度下降?

【问题讨论】:

    标签: erlang parallel-processing


    【解决方案1】:

    Erlang 的默认行为历来是运行一个调度程序,它基本上是一个本地 OS 线程,它选择 Erlang 任务从队列中运行。随着多核和多处理器系统的出现,运行时被扩展以利用。以-smp enabled 启动运行时将导致运行时创建多个调度程序,通常每个逻辑CPU 一个。您可以使用 -S 标志手动指定调度程序的数量,例如-S 16.

    这在Erlang Run-Time System Reference Manual 中有记录。

    可以在this discussion thread 中找到有关 SMP 支持的更深入讨论。

    编辑

    我还应该指出,从 R12B 开始,SMP 在支持它的平台上默认启用(相当于-smp auto 标志)。如果您对自己的运行时感到好奇,那么讨论线程中的以下引用将会很有趣:

    您可以从 “erl”命令。例如。 Erlang (BEAM) 仿真器版本 5.6.4 [source] [smp:4] [asynch-threads:0] .....

    上面的“[smp:4]”表明 SMP 虚拟机正在运行并且有 4 个调度程序。

    【讨论】:

    • 您的编辑对我来说是一个很好的输入。我的 erl 横幅上写着[smp:4:4]。所以我猜它已经为每个核心配备了一个调度程序。
    【解决方案2】:

    您看到如此少的并行性的原因是您的程序基本上是顺序的。所有工作都在 fib/3 函数的一个进程中完成。您生成的进程都只是发送一条消息然后死亡,并且生成进程同步等待这些消息,因此没有真正的并发性。您也可以直接使用这些值调用 loop/3 函数。

    否则就像其他人提到的那样,Erlang 会自动使用所有可用的多核并在可能的情况下在这些核之间分配进程。但是,在您的情况下,几乎不需要这样做并且没有收益,因此系统不会这样做。

    这实际上是编写并发应用程序时比较困难的事情之一。仅仅将事情分散到许多进程中是不够的,您实际上必须确保这些进程实际上是同时运行的。这意味着重新思考你的算法,这可能很困难。

    【讨论】:

      【解决方案3】:

      Erlang 不使用传统意义上的线程。 Erlang VM 为 CPU 的每个硬件核心创建一个系统线程。当您在 Erlang 中启动一个线程时,您实际上是在创建一个“任务”,这与系统线程不同。 Erlang 在 VM 内部管理这些任务。

      根据 VM 及其配置,这些任务可能会或可能不会映射到单个 CPU 内核,我相信这就是您在此处看到的。

      您可能会喜欢一篇有趣的博客文章here

      【讨论】:

        猜你喜欢
        • 2010-09-22
        • 2023-02-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-18
        • 2018-05-13
        • 1970-01-01
        • 2018-06-03
        相关资源
        最近更新 更多