Прим. переводчика: перевод выполнялся для erlanger.ru. Заранее приношу извинения за возможные неточности.
Этот бенчмарк производит следующие действия:
- симулирует создания и удаления большого количества актеров/процессов
- отправляет более чем 50 000 000 сообщений между актерами
- производит сложные численные вычисления для симуляции реальной рабочей нагрузки
Тестовая программа создает 20 колец по 50 актеров в каждом. Токен с начальным значением 10 000 передается по кольцу и его значение уменьшается на единицу при каждой итерации. Клиент, получивший токен, обязательно передает его следующему клиенту в кольце и завершает работу, если значение токена равно 0.
Следующий псевдокод иллюстрирует этот алгоритм:
chain_link(Next):
receive:
{token, N} =>
next ! {token, N}
if (N > 0) chain_link(Next)
worker(MessageCollector):
receive:
{calc, X} =>
MessageCollector ! {result, prime_factorization(X)}
master(Worker, MessageCollector):
5 times:
Next = self
49 times: Next = spawn(chain_link, Next)
Next ! {token, 10000}
Done = false
while not Done:
receive:
{token, X} =>
if (X > 0): Next ! {token, X-1}
else: Done = true
MessageCollector ! {master_done}
Каждое кольцо состоит из 49 chain_link актеров и одного master'а. Мастер перезапускает завершившие работу актеры пять раз. В целом, каждый мастер запускает 245 актеров, а программа запускает 20 master'ов. В общей сложности создается 4921 актер ((20+(20∗245)+1), из которых не более 1021 (20+20+(20∗49)+1) выполняются одновременно. Коллектор сообщений ждет, пока он не получит 100 (20∗5) результатов разложения на простые множители и сообщение о завершении работы от каждого master'а. Вычисление простых множителей используется для симуляции рабочей нагрузки.
Вычисление занимает около двух секунд на тестируемом оборудовании при реализации в виде цикла на C++. Решение с хвостовой рекурсией на Scala работает примерно с такой же скоростью, в то время как решение на Erlang'е выполняет ту же задачу за почти семь секунд.
Как и ожидалось, реализация на Scala, использующая потоки, показывает наихудший результат, хотя увеличение времени работы на восьми и более ядрах нас удивило. Реализация с использованием Akka значительно быстрее, чем обе реализции, использующие стандартные библиотеки.
Erlang показывает высокую производительность, даже учитывая тот факт, что он производит разложение на множители в три раза медленнее. Высокоэффективный планировщик Erlang'а, единственный планировщик с приоритетным прерыванием в бенчмарке, лучше других использует возможности для параллелизации, предоставляемые оборудованием.
Накладные расходы планировщика libcppa приводят к снижению производительности. Так, накладные расходы на аллокацию стека и переключение контекста потребляют до 10%-20% работы при использовании до 6 ядер CPU, которые являются практически пределом для планировщика.
Бенчмарк показывает, что libcppa является достаточно производительной библиотекой и работает на том же уровне, что существующие реализации actor model, но текущая реализация планировщика не может эффективно использовать более шести ядер.
Бенчмарк запускался на виртуальной машине с Линуксом, которой выделялись от 2 до 12 ядер на хост-системе, состоящей из двух шестиядерных 2.27 Гц процессоров Intel® Xeon®. Все значения являются средними значениями по результатам 5 запусков.
Исходники бенчмарка можно взять на GitHub'е: github.com/Neverlord/cppa-benchmarks.
Автор: dmitriid