вторник, 6 января 2009 г.

SObjectizer vs Kilim

Некто Sujit Pal в своем блоге привел результаты сравнения производительности нескольких агентных фреймворков для Java (сравнивались Kilim, Jetlang, Actor Guild, ActorFoundry, а так же Scala Actors из стандартной библиотеки языка Scala). Двумя наиболее быстрыми фреймворками оказались Kilim и Jetlang. Поскольку в блоге были опубликованы исходные тексты тестового приложения для Kilim, то я сравнил скорость работы SObjectizer 4.4.0-beta6 с Kilim 0.50.

 

На моей машине (Intel Core2Duo 2GHz, 2Gb RAM, WinXP) Kilim-приложение отработало за 12.870 секунды, а SObjectizer-приложение – за 11.828 (обе цифры являются усредненными по нескольким запускам результатами). В качестве JVM использовалась клиентская Java-машина из состава Java 1.6.0_07. SObjectizer-приложение компилировалось с помощью Visual C++ 2005. В процессе работы Kilim-приложение потребляло до 170Mb памяти, а SObjectizer-приложение – до 80Mb. Вот исходный текст SObjectizer-приложения.

 

Результаты тестов меня, честно говоря, несколько расстроили. Я рассчитывал на более серьезное преимущество SObjectizer за счет использования нативного кода. Но, видимо, в данном тесте, где создается большое количество мелких объектов-строк, сборщик мусора в Java делает выделение динамической памяти гораздо более быстрым, чем в C++.

 

Тем не менее, результат не самый плохой. Особенно с учетом того, как сообщения отсылаются в Kilim и в SObjectizer. В Kilim актеры должны владеть ссылками на конкретные объекты mailbox-ы, непосредственно в которые сообщения и помещаются. Тогда как в SObjectizer при отсылке сообщения есть только строковое имя агента-владельца сообщения, по которому SObjectizer сам находит получателя сообщения. Т.е. при отсылке сообщений в SObjectizer4 выполняется больше работы, чем в Kilim. Кроме того, в SObjectizer существуют понятия состояний и событий, которых нет в Kilim. А на работу с этими сущностями в SObjectizer так же тратится время и ресурсы.

 

Отдельно хотелось бы отметить один важный, на мой взгляд, момент. В упомянутых Java/Scala фреймворках процесс выборки сообщений агентами выглядит очень примитивным. Это обычный цикл, в начале которого из mailbox-а извлекается очередной объект-сообщение, после чего в виде if-ов, switch-ей или pattern matching-а (как в Scala) определяется, что же с этим сообщением нужно делать. Лично мне такой подход не нравится, я считаю его утомительным в написании, он череват ошибками и он сложен при сопровождении чужого кода. Я в очередной раз убедился, что лучше, когда фреймворк берет на себя задачу сопоставления очередного сообщения конкретному методу объекта.

 

Проведение данного сравнения так же привело меня к следующим выводам:

 

1. Ясно видно, что модель актеров сейчас стремительно набирает популярность. Об этом свидетельствует рост внимания к языку программирования Erlang, большое количество рассуждений на тему удобства модели актеров в блогах, растущее количество агентных фреймворков для Java. И это хорошо, т.к. формируется благоприятная для SObjectizer среда.

 

2. Если в области управляемых (managed) языков вроде Java/Scala/Python/Ruby наблюдается рост разнообразных (по мнению их авторов) агентных фреймворков, то для C++ такого роста не видно вообще. Что, на мой взгляд, благоприятно для SObjectizer – C++ умирать пока не собирается, и в C++ приложениях можно получить выгоды от использования модели актеров. Поэтому имеет смысл развивать SObjectizer именно как C++ инструмент.

 

3. В SObjectizer нужно вводить механизмы обмена сообщениями, которые не требуют поиска получателя сообщения по имени. Например, что-то типа mailbox-ов, как в Kilim, или каналов (channels, pipelines). Т.е. если двум агентам нужно обмениваться сообщениями друг с другом, они создают объект-канал, ссылка на который есть у обоих. И для отсылки сообщений они используют не send_msg, а метод записи сообщения в канал. Тем более, что каналы могут применяться так же для контроля за переполнением очереди сообщений агента.

4 комментария:

Dmitry Vyukov комментирует...

Первый вопрос, который у меня возникает в этом контексте, - насколько вообще важна "голая" производительность для агентного фреймворка? Очевидно, что принципиальна важна *масштабируемость*. Т.е. если есть пользовательское приложение, которое само по себе позволяет параллельное исполнение и масштабируемость, то фреймворк должен давать линейную масштабируемость при росте кол-ва ядер. А вот насколько важна при этом "голая" производительность? Т.е. даёт ли фремворк 10^6 сообщений/сек на холостом ходу или 2*10^6?
Было бы интересно услышать мнения.

Dmitry Vyukov комментирует...

А вообще, если "голая" производительнсть важна, то ты знаешь, что мы можем разбить все эти килимы-мылимы в пух и прах :)
Помнишь мы переписывались по поводу "15М сообщений в сек" - там по-моему окончательный вариант простенького фреймворка, который использует адресную отправку сообщений (через указатель на агента-получателя), показывал результат в 240М сообщений/сек на 4-ёх ядерном Core2Quad 2.4GHz. Там достаточно упрощенная версия, т.ч. я думаю, что полнофункциональная версия была бы раза в 2 медленнее, но всё равно это *очень* быстро.
Кстати, интересно было бы протестировать эти фреймворки именно на *масштабируемость*. Т.е. запустить на 1 ядре, на 2 ядрах, на 4 ядрах, на 8 ядрах. Кому нужен фремворк, который на одноялерном процессоре быстрее на 30%, зато на 4-ёх ядерном дергадировал на 50%?

Dmitry Vyukov комментирует...

Я бы приоритизировал требования к агентному фреймворку в контексте производительности следующим образом:
1. Линейная масштабируемость при увеличении числа ядер/процессоров. MUST BE!
2. Умный шедулинг: load-balancing, cohort-scheduling, cache-awareness, NUMA-awareness, system structure awareness, etc. На синтетических замерах это может не давать никакой выгоды, или даже замедление из-за дополнительных издержек; но в реальном приложении это может давать очень-и-очень существенное ускорение.
3. "Голая" производительность на отправку/получение сообщения.

Фактически Sujit Pal мерил самый НЕинтересный параметр. Ну по-крайней мере он не особо интересный, если разница не очень большая, что можно сказать про kilim, jetlang, scala react, actor foundary.

eao197 комментирует...

Дима, твои комментарии, конечно, справедливы. Но для проведения серьезного сравнения трабуются и более серьезные усилия. Почему Sujit Pal не провел его, я не могу судить, зато могу сказать, почему я не проводил такого сравнения :) Ну лень, в общем-то, было. Праздники, к тому же.

Подтолкнуло меня к этому сравнению обилие ссылок на разные блог-посты, в которых обсуждаются (точнее, просто упоминаются) actor-фреймворки, которые для Java появляются как грибы после дождя. Вот и захотелось приобщить SObjectizer к этому hype. Да и самому увидеть результаты сравнения SObjectizer-а с ними.

Если же говорить серьезно, то мне кажется, что не очень правильно рассматривать actor model только как модель для concurrency. Декомпозиция на актеры возможна и в "линейных" задачах, где, например, идет обработка некоторого потока данных (т.е реализация pipes-filters архитектуры на агентах и сообщениях). В этих случаях важна будет и тупая производительность фреймворка на одном ядре. Хотя таких случаев, наверное, меньше.