Проблема с производительностью: работа с интенсивным использованием ЦП лучше работает с большим параллелизмом в Erlang

tl; dr
Я получаю лучшую производительность с моей программой на erlang, когда выполняю задачи, интенсивно использующие процессор, с более высокой степенью параллелизма (например, 10 КБ сразу по сравнению с 4). Почему?


Я пишу фреймворк сокращения карты, используя erlang, и провожу тесты производительности.

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

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

Фрагмент кода:

%% When I remove the comment, I get significant performance boost (90% -> 96%)
%% spawn_link(fun()->
                %% One invocation uses around 250ms of CPU time
                do_map(Map, AssignedSet, Emit, Data),
                Manager ! {finished, JobId, self(), AssignedSet, normal},
%%       end),

По сравнению с тем, когда я выполняю те же вычисления в тесном цикле, я получаю 96% пропускной способности (эффективности) с использованием метода «немедленного порождения» (например, 10000 карт сокращают задания, выполняющиеся полностью параллельно). Когда я использую метод «один за другим», я получаю только около 90%.

Я понимаю, что Erlang должен хорошо справляться с параллельными вещами, и я впечатлен тем, что эффективность не меняется, даже если я выполняю запросы уменьшения карты на 10К одновременно, а не на 100 и т. Д.! Однако, поскольку у меня всего 4 ядра ЦП, я бы ожидал получить лучшую пропускную способность, если буду использовать более низкий уровень параллелизма, например 4 или, может быть, 5.

Как ни странно,мое использование ЦП выглядит очень похоже в двух разных реализациях (почти полностью привязано к 100% на всех ядрах). Разница в производительности достаточно стабильная. Т.е. даже когда я выполняю всего 100 заданий по сокращению карты, я все равно получаю около 96% эффективности с методом «немедленного появления» и около 90% при использовании метода «по одному». Точно так же, когда я тестирую 200, 500, 1000, 10 тысяч заданий.

Сначала я подозревал, что причиной является постановка в очередь в очереди рабочего процесса, но даже когда у меня должно быть только около 25 сообщений в очереди рабочего процесса, я все равно вижу более низкую производительность. 25 сообщений кажутся довольно маленькими для того, чтобы вызвать засорение (я выполняю выборочное сопоставление сообщений, но не так, чтобы процесс возвращал сообщения в очередь).

Я не уверен, что мне делать дальше. Я что-то делаю не так, или я что-то совсем упускаю ??

ОБНОВЛЕНИЕ

Я провел еще несколько тестов и обнаружил, что разница в производительности может исчезнуть в зависимости от условий (особенно в зависимости от того, на сколько рабочих процессов я делю статические данные). Похоже, мне нужно еще многому научиться!

5
задан Enno Shioji 27 July 2011 в 02:13
поделиться