В SObjectizer уже довольно давно существует такая штука, как work thread activity tracking. Но этот механизм дает только самую общую информацию о том, сколько событий было обработано на конкретном диспетчере (точнее, на конкретной нити диспетчера), сколько времени это заняло, сколько раз приходилось ждать поступления событий и сколько времени это ожидание заняло. Механизм самый базовый. Но уже, при необходимости, помогающий посмотреть на то, куда и как тратится время в программе.
Возможно, пришло время расширить этот механизм и добавить в SObjectizer сбор более детальной информации о том, куда тратится время. На данный момент я вижу два разных варианта. Оба кратко описаны под катом. Если кому-то интересно, то прошу заглянуть под кат и высказать свое мнение о том, какой из вариантов был бы предпочтительнее для вас. Или, может быть, предложить свой вариант.
Важный момент. Интенсивность работы над этой задачей будет зависеть от того, какой интерес эта задача вызовет у публики. Если никто не заинтересуется, то мы ничего и не будем делать до тех пор, пока нам самим что-нибудь такое не потребуется. Ну а если, напротив, тема вызовет интерес, то мы выделим ресурсы на ее решение. Так что если вы ничего не скажете, то ничего и не получите, все просто ;)
Итак, есть два, как мне кажется, принципиально разных подхода.
Первый подход состоит в том, чтобы просто расширить существующий механизм work thread activity tracking дополнительной информацией. Предположительно, в сообщение work_thread_activity будет добавлено еще одно поле. Что-то вроде:
struct event_activity_key_t; { // Тип сообщения, которое обрабатывал агент. const std::type_index m_msg_type; // Некий идентификатор агента, который обрабатывал сообщение. // Актуальность этого идентификатора не зависит о того, жив ли // еще агент или же он уже прекратил свое существование. const so_5::stats::agent_identity_t m_agent; ... }; // Тип словаря, содержащего статистику работы агентов. using activity_map_t = std::map< // В качестве ключа пара из msg_type/agent_id. event_activity_key_t, // В качестве значения -- статистика по этому агенту // и этому типу сообщения. so_5::stats::activity_stats_t>; struct work_thread_activity : public message_t { ... // Все поля, которые уже есть. // Детальная статистика по событиям, которые были обработаны // агентами на этой рабочей нити. activity_map_t m_agents_activity; }; |
Этот вариант сразу же напрашивается когда речь заходит о расширении функциональности work thread activity tracking. Но здесь есть свои неудобства:
Во-первых, существующее поле work_thread_activity::m_stats дает статистику "с накапливающимся итогом". Т.е. в ней содержатся данные от момента начала работы рабочей нити. Соответственно, такие значения, как m_stats::m_working_stats::m_count или m_stats::m_waiting_stats::m_total_time будут монотонно возрастать. Тогда как содержимое m_agents_activity будет обновляться после каждого такта выдачи статистики. Например, если статистика выдается раз в минуту, то m_agents_activity будет содержать только информацию о событиях за прошедшую минуту. А информация о том, что было две или три минуты назад, будет потеряна.
Причем здесь все не так просто. Например, запускается обработчик события, который работает 15 минут подряд. Тогда как статистика будет выдаваться раз в минуту. В этом случае статистика за первую минуту будет содержать информацию о предыдущих обработчиках, а так же об этом обработчике (но длительность работы этого обработчика будет меньше минуты, что естественно, так как он только начал работать). В статистике за вторую минуту будет информация только об этом обработчике. Но будет указано, что он работает больше минуты. В статистике за третью минуту опять же будет информация только об этом обработчике, но время его работы будет уже больше 2-х минут. И т.д. И только в статистике за 16-ю минуту появится информация о других обработчиках, которые запустились после этого длительного обработчика. При этом для длительного обработчика будет указано, что он работал 15 минут.
В общем, получается, что интерпретировать информацию из work_thread_activity станет сложнее, т.к. нужно будет понимать, какая часть статистики как считается и какие в ней могут быть "всплески".
Во-вторых, этот подход дает возможность только получать статистику по событиям. Не более того. А вот, скажем, точный трейс работы, т.е. перечень событий и их длительностей, а так же их порядок, восстановить уже не получится. Хотя, в каких-то случаях именно эта информация и будет нужна.
Поэтому-то рассматривается еще и второй вариант.
Второй вариант предполагает наличие задаваемого пользователем монитора активности. Интерфейс которого может выглядеть следующим образом:
class activity_monitor_t { public: virtual ~activity_monitor_t() = default; // Этот метод будет вызван перед тем, как агенту будет передана // заявка для обработки. virtual void on_evt_processing_started( // Идентификатор рабочей нити, на которой происходит запуск обработчика. so_5::current_thread_id_t thread_id, // Описание заявки, которая будет обработана. const so_5::execution_demand_t & demand ) noexcept = 0; // Этот методо будет вызван после того, как агенту будет передана // заявка для обработки. virtual void on_evt_processing_finished( // Идентификатор рабочей нити, на которой запускался обработчик. so_5::current_thread_id_t thread_id, // Описание заявки, которая была обработана. const so_5::execution_demand_t & demand ) noexcept = 0; }; |
Пользователь реализует свой класс с интерфейсом activity_monitor_t, создает объект этого класса и передает этот объект в параметрах SObjectizer Environment:
class my_tracing_activity_monitor_t : public so_5::activity_monitor_t { ... }; int main() { my_tracing_activity_monitor_t monitor; so_5::launch( [](so_5::environment_t & env) {...}, [&](so_5::environment_params_t & params) { // Добавляем монитор в SOEnv для трассировки обработчиков событий. params.add_activity_monitor(monitor); ... }); } |
В этом случае SObjectizer будет вызывать методы monitor.on_evt_processing_started() и monitor.on_evt_processing_finished() перед и после вызова любого события у любого агента.
Думаю, что можно будет сделать так, чтобы пользователь мог более гибко управлять тем, какие агенты попадают в монитор активности, а какие нет. Например, можно назначить монитор активности на весь SObjectizer Environment, но при этом запретить использование монитора для какого-то отдельного диспетчера. Или, напротив, не назначать монитор на весь SObjectizer, а назначить монитор только для определенных диспетчеров (причем для каждого диспетчера можно назначить свой собственный монитор).
В принципе, мониторы можно объединять в цепочки. Т.е. задать несколько мониторов. Тогда методы on_evt_processing_start/finish будут последовательно вызываться у каждого из них. Например, пользователь сможет создать два монитора: один собирает статистику, второй контролирует длительность работы обработчиков событий и генерирует тревоги, если какой-то обработчик работает слишком долго. Оба эти монитора можно будет задать для SObjectizer Environment:
class my_stats_collector_monitor_t : public so_5::activity_monitor_t { ... }; class my_duration_checker_monitor_t : public so_5::activity_monitor_t { ... }; int main() { my_stats_collector_monitor_t stats_collector; my_duration_checker_monitor_t duration_checker; so_5::launch( [](so_5::environment_t & env) {...}, [&](so_5::environment_params_t & params) { // Добавляем мониторы в SOEnv. params.add_activity_monitor(stats_collector); params.add_activity_monitor(duration_checker); ... }); } |
Этот вариант так же предполагает, что пользователь сам сможет создавать те мониторы, которые ему нужны. Ну и, плюс к тому, можно будет сделать какой-то готовый монитор, собирающий статистику. Этот монитор сможет удовлетворить пользователей в каких-то простых случаях.
Второй вариант гибче первого, т.к. позволяет делать разные вещи. Например, снимать трейс работы агентов. Или же собирать точные данные о длительностях работы обработчиков событий. Тут уж насколько у кого фантазии хватит.
Плюс к тому, он ортогонален существующему механизму work thread activity tracking. Т.е. можно не включать work thread activity tracking вообще, но добавить мониторы и получать информацию о работе агентов. Или можно включить work thread activity tracking одновременно с использованием мониторов активности (тогда в work_thread_activity начнут отражаться и накладные расходы мониторов, т.к. для рабочей нити это будет выглядеть как часть обработчика событий).
Но зато во втором варианте не будет информации о том, на каком диспетчере работает агент. В монитор попадает только Thread ID, но не информация о диспетчере. Можно ли передать в монитор еще и какое-то описание диспетчера -- это отдельный вопрос, требующий проработки.
Вот такие два варианта пока видны. Оба, в принципе, выглядят вполне себе реализуемыми. Хотя изрядное количество труда придется вложить и туда, и туда. И чтобы не тратить время и силы на то, что может никому не понадобится, прошу заинтересовашихся высказываться: нужна ли вам подобная функциональность в SO-5? Если нужна, то в каком виде? В том числе можно предложить и свой вариант.
PS. По поводу работы над параллельными состояниями. Там пока все довольно туго. Сложно вписать параллельные состояния в существующий в SO-5.5 механизм состояний, подписок и поиска обработчиков. Хорошей идеи о том, как это сделать не поломав совместимость и не увеличив накладные расходы на объекты so_5::state_t, пока не появилось. Поэтому приоритет этой задачи понизился.
Комментариев нет:
Отправить комментарий