Подтвердить что ты не робот

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

Я изучаю erlang и очень впечатлен, насколько легко распараллелить работу. Чтобы немного попрактиковаться, я выкопал хорошую последовательность Fibanocci ole. В следующем коде я стараюсь использовать параллелизацию, вычисляя дорогостоящие продукты по три за раз.

-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 node или моя ОС (ubuntu с 2.6.38 в моем случае)?
  • Я теряю скорость из-за того, что два или три ядра работают на холостом ходу?
4b9b3361

Ответ 1

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

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

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

ИЗМЕНИТЬ

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

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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