Erlang на многоядерном процессоре

Я изучаю erlang и очень впечатлен тем, как легко распараллелить работу. Чтобы немного попрактиковаться, я откопал хорошую последовательность Ole 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), работает только от одного до двух ядер, а другие работают на холостом ходу вокруг.

enter image description here

мои вопросы:

  • кто решает, на сколько ядер распределены рабочие потоки? Узел erlang или моя ОС (ubuntu с 2.6.38 в моем случае)?
  • Я теряю скорость из-за того, что два или три ядра работают на холостом ходу?

3 ответов


поведение по умолчанию Erlang исторически заключалось в запуске одного планировщика, который в основном является родным потоком ОС, который выбирает задачи Erlang для запуска из очереди. С появлением многоядерных и многопроцессорных систем среда выполнения была расширена, чтобы воспользоваться преимуществами. Запуск среды выполнения с помощью -smp enabled заставит среду выполнения создать несколько планировщиков, обычно по одному на логический процессор. Вы можете вручную указать количество планировщиков с -S флаг например -S 16.

это описано в Erlang Run-Time System Справочное Руководство.

более глубокое обсуждение поддержки SMP можно найти в этой теме.

редактировать

Я должен также отметить, что, начиная с R12B, SMP включен по умолчанию на платформах, которые его поддерживают (эквивалентно -smp auto флаг). Если вам интересно узнать о вашей собственной среде выполнения, приведенная ниже цитата из потока обсуждения будет интересно:

вы можете увидеть, что было выбрано в первой строке распечатки из команда "Ерл". Например. Erlang (BEAM) emulator версия 5.6.4 [источник] [smp:4] [asynch-потоки:0] .....

"[smp:4]" выше говорит, что SMP VM запущен и с 4 планировщиками.


причина, по которой вы видите так мало параллелизма, заключается в том, что ваша программа в основном последовательна. Вся работа выполняется в одном процессе в функции fib/3. Процессы, которые вы порождаете, просто отправляют сообщение, а затем умирают, и процесс нереста синхронно ждет этих сообщений, поэтому реального параллелизма нет. Вы можете просто вызвать функцию loop/3 непосредственно с этими значениями.

в противном случае, как упоминали другие, Erlang автоматически использует все несколько ядер доступны и распределяет процессы между ними, где это возможно. В вашем случае, однако, нет необходимости делать это и никакой выгоды, поэтому система этого не делает.

Это на самом деле одна из самых сложных вещей в написание параллельных приложений. Недостаточно просто распределить вещи во многие процессы, вы действительно должны убедиться, что эти процессы действительно работают одновременно. Это означает переосмысление ваших алгоритмов, что может быть сложно.


Erlang не использует потоки в традиционном смысле. Erlang VM создает один системный поток для каждого аппаратного ядра процессора. Когда вы запускаете поток в Erlang, вы действительно создаете "задачу", которая отличается от системного потока. Erlang управляет этими задачами внутри виртуальной машины.

в зависимости от VM и его конфигурации эти задачи могут или не могут быть сопоставлены с отдельными ядрами процессора, которые, я считаю, вы видите здесь.

есть интересное статья в блоге вам может понравиться здесь.