Erlang调度如何为多核CPU计算机工作?

t1rydlwq  于 2022-12-08  发布在  Erlang
关注(0)|答案(3)|浏览(139)

我正在学习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节点还是我的操作系统(在我的例子中是ubuntu和2.6.38)?
  • 是否由于两个或三个内核处于空闲状态而导致速度下降?
gkn4icbw

gkn4icbw1#

Erlang's default behavior has historically been to run one scheduler, which is basically a native OS thread, which chooses Erlang tasks to run from a queue. With the advent of multi-core and multi-processor systems, the runtime was extended to take advantage. Starting the runtime with -smp enabled will cause the runtime to create multiple schedulers, usually one per logical CPU. You can manually specify the number of schedulers with the -S flag e.g. -S 16 .
This is documented in the Erlang Run-Time System Reference Manual .
A deeper discussion of SMP support can be found in this discussion thread .

EDIT

I should also point out that, as of R12B, SMP is enabled by default on platforms that support it (equivalent to the -smp auto flag). If you're curious about your own runtime, the following quote from the discussion thread will be of interest:
You can see what was chosen at the first line of printout from the "erl" command. E.g. Erlang (BEAM) emulator version 5.6.4 [source] [smp:4] [asynch-threads:0] .....
The "[smp:4]" above tells that the SMP VM is run and with 4 schedulers.

bogh5gae

bogh5gae2#

你看到这么少并行性的原因是你的程序基本上是顺序的。所有的工作都在fib/3函数的一个进程中完成。你所产生的进程都只是发送一个消息,然后死亡,而产生的进程同步地等待这些消息,所以没有真实的的并发性。你也可以直接用这些值调用loop/3函数。
另外,正如其他人提到的,Erlang会自动使用所有可用的多个内核,并在可能的情况下将进程分布在这些内核上。但在您的情况下,几乎没有必要这样做,而且没有任何好处,因此系统不会这样做。
这实际上是编写并发应用程序中比较困难的事情之一。仅仅将事情分散到许多进程中是不够的,你实际上必须确保这些进程实际上是并发运行的。这意味着重新思考你的算法,这可能是困难的。

3yhwsihp

3yhwsihp3#

Erlang并不使用传统意义上的线程,Erlang VM为CPU的每个硬件核心创建一个系统线程,当你在Erlang中启动一个线程时,你实际上是在创建一个“任务”,它与系统线程不同,Erlang在VM内部管理这些任务。
根据虚拟机及其配置的不同,这些任务可能会也可能不会Map到各个CPU内核,我相信这就是您在这里看到的。
有一篇有趣的博客文章,你可能会喜欢here

相关问题