Как заставить параллельную функцию (pmap) использовать все ядра в Elixir?

Я новичок в Elixir и начинаю читать превосходный Programming Elixir Дэйва Томаса. Мне было любопытно, как далеко я смогу зайти в параллельном режиме функции «pmap», поэтому я многократно увеличивал количество элементов до квадрата с 1000 до 10 000 000. Из любопытства я наблюдал за выводом htop, когда я это делал, обычно достигая максимума при загрузке ЦП, аналогичной показанной ниже:

htop output

Показав пример в книге, Дэйв говорит:

И да, я только что запустил 1000 фоновых процессов и использовал все ядра и процессоры на своей машине.

У меня вопрос, почему на моей машине светятся только ядра 1, 3, 5 и 7? Я предполагаю, что это связано с тем, что мой iex процесс является всего лишь одним процессом уровня ОС, а OSX управляет охватом этого процесса. Это то, что здесь происходит? Есть ли способ обеспечить использование всех ядер для задач, требующих высокой производительности?


person user456584    schedule 24.04.2016    source источник
comment
Какая первая строка вывода iex? то есть: Erlang / OTP 18 [erts-7.3] [источник] [64-бит] [smp: 4: 4] [асинхронные потоки: 10] [hipe] [kernel-poll: false] [dtrace]   -  person Thiago Silveira    schedule 24.04.2016
comment
@ThiagoSilveira Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]   -  person user456584    schedule 24.04.2016


Ответы (2)


Отличный комментарий @Thiago Silveira о первой строке вывода iex. Часть [smp:8:8] говорит, сколько процессов уровня операционной системы использует Erlang. Вы можете контролировать это с помощью флага --smp, если хотите его отключить:

iex --erl '-smp disable'

Это гарантирует, что у вас будет только один системный процесс. Вы можете добиться аналогичного результата, оставив включенной симметричную многопроцессорность, но установив напрямую NumberOfShcedulers:NumberOfSchedulersOnline.

iex --erl '+S 1:1'

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

:erlang.system_info(:schedulers_online)

Чтобы ответить на ваш вопрос о производительности. Если ваши процессоры не работают на полную мощность (100%) и ни один из них ничего не делает (0%), то, вероятно, более равномерное распределение нагрузки не ускорит процесс. Почему?

Использование ЦП измеряется путем проверки состояния процессора во многие моменты времени. Это состояния либо «работает», либо «простаивает». 82% использования ЦП означает, что вы можете выполнять еще пару задач на этом ЦП, не замедляя выполнение других задач.

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

Следующее, что может вызвать такое большое расхождение между четными и нечетными ядрами, - это Hyper Threading. На моем двухъядерном процессоре htop показывает 4 логических ядра. В вашем случае у вас, вероятно, есть 4 физических ядра и 8 логических из-за HT. Возможно, вы используете свои физические ядра на 100%.

Другое дело: pmap нужно вычислять результат в отдельном процессе, но в конце он отправляет его вызывающей стороне, что может быть узким местом. Чем больше вы отправляете сообщений, тем меньше вы можете использовать процессор. Вы можете ради развлечения дать процессам задачу, которая действительно потребляет много ресурсов процессора, например вычисление функции Акермана. Вы даже можете рассчитать, какая часть вашей работы является последовательной, а какая параллельной, используя закон Амдала и измеряя выполнение. раз для разного количества ядер.

Подводя итог: загрузка ЦП на скриншоте выглядит просто великолепно! Вам не нужно ничего менять для более ресурсоемких задач.

person tkowal    schedule 24.04.2016

Параллелизм - это не параллелизм

Чтобы получить хорошую параллельную производительность от кодирования Elixir / BEAM, вам необходимо иметь некоторое представление о том, как работает планировщик BEAM.

Это очень упрощенная модель, но планировщик BEAM дает каждому процессу 2000 сокращений, прежде чем он заменяет процесс для следующего процесса. Редукции можно рассматривать как вызовы функций. По умолчанию процесс запускается в ядре / планировщике, которое его породило. Процессы перемещаются между планировщиками только в том случае, если очередь невыполненных процессов накапливается в данном планировщике. По умолчанию BEAM запускает поток планирования на каждом доступном ядре.

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

Другая вещь, о которой следует знать, - это то, что некоторые части BEAM используют цикл вращения / ожидания при ожидании работы, и это может исказить использование, когда вы используете такой инструмент, как htop, для проверки использования ЦП. Вы получите гораздо лучшее представление о производительности вашей программы, используя :observer.

person Fred the Magic Wonder Dog    schedule 25.04.2016