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 сообщений кажутся довольно маленькими для того, чтобы вызвать засорение (я выполняю выборочное сопоставление сообщений, но не так, чтобы процесс возвращал сообщения в очередь).
Я не уверен, что мне делать дальше. Я что-то делаю не так, или я что-то совсем упускаю ??
ОБНОВЛЕНИЕ
Я провел еще несколько тестов и обнаружил, что разница в производительности может исчезнуть в зависимости от условий (особенно в зависимости от того, на сколько рабочих процессов я делю статические данные). Похоже, мне нужно еще многому научиться!