пятница, 20 марта 2009 г.

Несколько интересных ссылок

Серьезных, по работе…

Оказывается, есть Multicore Association, которая выпустила спецификацию для Communication API (API для обмена сообщениями на мультиядерных архитектурах). Вот ведь, я даже не слышал никогда…

Не очень серьезных, вокруг работы…

На RSDN есть такой очень знаковый персонаж – Павел Дворкин. Иногда он выдает в форуме “Философия программирования” замечательные посты, которые интересно обсуждать и обдумывать. Вот очередной такой пост: Взгляд со стороны. Имхо, Павел смешивает мелкое с мягким (т.е. приводит некорректное сравнение), но посыл верный: пока в программировании наблюдается очень сильная многостаночность. Мне самому интересно, надолго ли?

Совсем не серьезных, но показывающих, что границы человеческой креативности еще не достигнуты…

Вот как программисты запрещают компьютеру уходить в режим “сна”.

А вот так ремонтируют систему вентиляции в серверной.

Ну а здесь вообще улет: вот до чего могут дойти пастухи овец (найдена у thesz).

SObjectizer4 and Multicore Scalability (век живи – век учись)

В рамках работ над седьмой бетой SObjectizer 4.4.0 потратил несколько дней на то, чтобы выяснить, почему SObjectizer 4.4.0-b6 на тесте customer_ring показывает серьезное падение производительности (результаты можно увидеть здесь).

Дело оказалось в блокировке ядра SObjectizer4, которая выполнялась внутри send_msg для поиска агента-владельца сообщения, поиска экземпляра сообщения и генерации заявок диспетчерам. Блокировка была простая, на основе обычного mutex-а. Поэтому выполнять все эти операции могла только одна нить. Как следствие, на двух ядрах две рабочие нити постоянно конкурировали друг с другом за этот mutex. И пока одна нить занималась отсылкой сообщения, вторая нить ждала первую на входе в send_msg.

В результате я перевел замок ядра SObjectizer4 с обычного mutex-а на reader-writer mutex (с помощью класса ACE_RW_Thread_Mutex). Общая производительность SObjectizer 4.4.0-b7 несколько снизилась по сравнению с 4.4.0-b6. Зато тест customer_ring на двух ядрах показывает увеличение пропускной способности, а не снижение. И, мне кажется, это очень важно.

Вот конкретные цифры для теста customer_ring (на Core2Duo):

Параметры теста

SO 4.4.0-b6

SO 4.4.0-b7

-N 10000 –M 100 395K msg/sec 361K msg/sec
-N 10000 –M 100 –D AG –G 2 172K msg/sec 392K msg/sec
-N 10000 –M 100 –D AG –G 4 250K msg/sec 294K msg/sec
-N 1000 –M 100 –D AO 425K msg/sec 425K msg/sec

Причем ACE_RW_Thread_Mutex, насколько я могу судить, далеко не самый быстрый вариант reader-writer mutex-а (по крайней мере под Windows). На тестах, когда я вместо ACE_RW_Thread_Mutex использовал счетчики на основе ACE_Atomic_Op, получались результаты в районе 600K msg/sec для двух рабочих нитей. Но проверенной реализации эффективного RW_Mutex-а на основе атомарных операций у меня нет, поэтому пока будет задействован ACE_RW_Thread_Mutex. (Я буду признателен, если кто-нибудь укажет мне на OpenSource реализацию быстрого, кроссплатформенного RW_Mutex-а под BSD/MIT-подобными лицензиями. Или даже под GPL-лицензией, чтобы хотя бы идею оттуда можно было взять.)

Блокировка ядра в SObjectizer 4.4.0-b7 в результате усложнилась. Раньше существовало всего два вида блокировок: блокировка для однофазной операции (например, для подписки события агента или отсылки не отложенного и не периодического сообщения) и блокировка для многофазной операции (например, для регистрации кооперации или отсылки отложенного/периодического сообщения). Теперь добавился еще один вид блокировки: для read-only операции (таковой сейчас является отсылка не отложенного и не периодического сообщения). Было бы желательно еще иметь и read-only многофазную операцию. Но я не придумал, как же просто и эффективно ее реализовать :( Поэтому сейчас отсылка отложенного/периодического сообщения выполняется как многофазная операция и эта операция блокирует все остальные операции (т.е. нельзя параллельно отсылать несколько отложенных сообщений). Не самая лучшая ситуация, конечно. Но хотя бы радует то, что отложенные/периодические сообщения отсылаются гораздо реже обычных сообщений.

Какой же вывод из всего вышеизложенного? А вывод очень простой: архитектура SObjectizer4, рассчитанная на использование одного общего системного словаря и адресация агентов через их имена, оказалась не приспособленной к масштабированию на multicore процессорах. Наверное, в рамках SObjectizer4 еще возможны какие-то оптимизации, способные улучшить производительность SObjectizer4 на нескольких ядрах. Но не на порядки.

Что ж, урок хоть и неприятный, но крайне полезный. Это означает, что SObjectizer5 должен строится совсем на других принципах. Наверное, вот так я это представляю себе сейчас:

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

Во-вторых, никаких текстовых имен сообщений. Каждое сообщение, которое можно будет отсылать в SObjectizer, будет представлено своим “доменом” – экземпляром специального типа. Например, есть в программе необходимо сообщения типа msg_do_something, то в программе необходимо будет создать домен для этого сообщения. Например, вот так:

struct msg_do_something : public so_5::message_t
  { ... };
so_5::message_domain_t< msg_do_something > msg_do_something_domain;

Подписка и широковещательная отсылка сообщения будет происходить через домен сообщения. Скажем, вот так:

// Подписка на сообщение.
msg_do_something_domain.subscribe( my_agent_ref, &my_agent_t::evt_do_something );

// Широковещательная отсылка сообщения.
std::auto_ptr< msg_do_something > msg( new msg_do_something( ... ) );
msg_do_something_domain.send( msg );

// Целенаправленная отсылка сообщения.
std::auto_ptr< msg_do_something > msg( new msg_do_something( ... ) );
msg_do_something_domain.send( msg, my_agent_ref );

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

// Если сообщение не отложенное, то нужно сразу отослать его,
// а уже затем разбираться с тем, периодическое ли оно или нет.
// После окончания этой операции ядро гарантированно должно
// быть разблокированно.
if( !delay )
	{
		external_references += deliver_msg_on_blocked_kernel(
				kernel_lock,
				to_deliver,
				insend_dispatching_enabled );
	}
else
	kernel_lock.unlock();

so_4::rt::impl::kernel_t::m_disp->push_delayed_msg(
		timer_msg_data,
		timer_delay,
		period );

Между if-ом и обращением к push_delayed_msg может произойти операция shutdown на другой нити приложения, и тогда указатель kernel_t::m_disp окажется нулевым. Со всеми вытекающими последствиями. Для того, чтобы такой ситуации не происходило в SObjectizer4 есть специальный счетчик многофазных операций и специальное событие, которое выставляется, когда shutdown разрешен.

Поскольку в SObjectizer5 нужно уйти от глобальных счетчиков и блокировок, нужно как-то решить проблему shutdown-а посреди длительной операции. Грубо говоря, чтобы kernel_t::m_disp не обнулялся пока message_domain занимается генерацией заявок.

Может быть, данная проблема вообще не возникнет, если гарантировать, что все ссылки, которыми располагает агент, не могут измениться в течении всей жизни агента. Т.е. пусть будет интерфейс so_5::runtime_t. Программист должен будет создать объект этого типа в самом начале работы приложения. Затем, ссылка на этот объект будет передаваться в конструкторе всем создающимся в программе агентам. Скажем, в базовом классе so_5::agent_t будет единственный конструктор, требующий ссылку на so_5::runtime_t:

class agent_t
	{
	private :
		so_5::runtime_t & runtime_;
	public :
		agent_t( so_5::runtime_t & rt ) : runtime_( rt )
			{}
		...
		so_5::runtime_t & runtime() const { return runtime_; }
	};

После чего везде, где у нас есть ссылка на агента, можно будет безопасно выполнять обращения к runtime:

// Где-то в дебрях отсылки отложенного сообщения...
receiver.runtime().push_delayed_msg( ... );

В таком случае на программисте будет лежать задача обеспечения корректности ссылки на so_5::runtime_t в течении всего времени работы программы. Что, как мне думается, не очень сложно. Зато в ядре SObjectizer, мне кажется, будет проще обеспечить завершение ранее начатых длительных операций при выполнении операции shutdown.

среда, 18 марта 2009 г.

Работать по 10-12 часов в день?

В своей предыдущей заметке я сказал, что когда меня захватывает какая-то идея, то мне проще работать по 10 часов в день, чем следовать обычному 8-ми часовому рабочему дню и пятидневной рабочей неделе. Но нужно обязательно сказать, что далеко не всегда можно плодотворно отработать 10 часов в день. Продолжительность продуктивного рабочего дня сильно зависит от того, чем именно приходится заниматься.

Итак все начинается с этапа “мозгового штурма” или “выкуривания” идеи (от “эту тему нужно покурить”). Как правило, здесь не требуется программировать, зато приходится много думать. Иногда очень много думать. Но я не могу напряженно думать над чем-то более 3-4 часов подряд. Получается, что на стадии обдумывания идеи плодотворный рабочий день у меня длится не более четырех (!) часов. Да, всего четыре часа. Временами еще меньше.

Затем наступает этап воплощения идеи в коде. Здесь так же редко встречаются 10-часовые рабочие дни. Скорее речь идет о 5-6, может быть, 7-ми часах нормального кодирования в день. Тогда пишется хороший код, с комментариями, с тестами, и у меня самого нет претензий к качеству написанного кода.

После того, как код готов, наступает этап документирования. Здесь так же продолжительность плодотворного рабочего дня сильно не дотягивает до стандартных 8 часов. Часов 6 на написание документации в день – это еще можно выдержать, хотя и не очень просто. Иногда бывало и побольше, но впечатления были плохими -- “выжатый лимон” это очень точное выражение.

Так когда же можно плодотворно работать по 10 и более часов? Размышляя над этим я вспомнил три ситуации, когда время летит очень-очень быстро.

Во-первых, можно убить массу времени на проведение различных экспериментов. Например, нужно использовать новую библиотеку в проекте или нужно выяснить, как работать с каким-то устройством. Тут просто не замечаешь, как за написанием небольших тестиков уходит один час за другим. Ведь это же так интересно: а что будет, если вот этот параметр передать? А почему ничего не напечатало? А если вот так? Опять нет! А что Google по этому поводу говорит? Ага, тогда вот так. Опять нет! Ну тогда вот так! Опаньки! А чего же сразу не заработало, я же так в самый первый раз писал! Да, да, я знаю, что уже десятый час, еще минут пятнадцать и поеду домой…

Во-вторых, отладка. В поисках причин различных багов время летит совершенно незаметно. Казалось бы, только недавно пришел на работу, а уже поздний вечер, уже пора идти домой, а хочется попробовать еще вот эту комбинацию входных данных. Поскольку сейчас уже точно получится выловить ошибку! А, не получилось. Значит можно вот так попробовать. Сейчас уже точно получится. Опять не получилось… Ну сейчас-то уж точно… Хорошо, когда такая отладка идет не в ночь перед демонстрацией продукта заказчику. Однажды я убил три дня на поиск чужого бага, когда до сдачи оставалось что-то около недели и это при том, что из-за этой ошибки мы еще ни разу не делали полных тестов всей системы. Я бы тогда сидел на работе и сутки напролет, но предприятие было режимное, и в 22:00 помещение нужно было покинуть…

В-третьих, профилирование и оптимизация. Это занятие сочетает в себе “лучшие” стороны двух предыдущих пожирателей времени. Плюс к тому, в профилировании есть объективная составляющая, очень успешно убивающая время: получение результатов профилирования. У меня как-то была программа, которая под профайлером отрабатывала за пятнадцать минут (ну или около того). Уменьшить объем вычислений нельзя было, т.к. тогда не достигалось нужного количество проходов по проблемным участкам кода. Вот и получалось, что я делал небольшую правку в коде, потом пятнадцать минут ждал результата. Потом еще одну правку и еще пятнадцать минут ожидания. Не удивительно, что в таком режиме рабочие дни сильно превышали 8-мь часов.

Получается, что при нормальном развитии событий продолжительные рабочие дни – это редкость. Сначала “мозговой штурм”, когда работать больше четырех часов не получается физически. Иногда эта стадия разбавляется экспериментами. Тогда несколько дней можно просидеть на работе и по 10 часов. Затем кодирование. Во время которого случаются и периоды отладки, и периоды профилирования. После чего документирование, но там опять работа идет не более 5-6 часов в день.

Важное дополнение. Речь шла именно о времени работы над проектов. А не о времени работы в офисе. Это очень разные вещи. Поэтому, когда мне нужно что-нибудь важное сделать, я стараюсь в офисе не показываться :)

понедельник, 16 марта 2009 г.

По следам прошедшего воскресения

О размеренности…

Выходные – это, пожалуй, самые нелюбимые мои дни недели. Я вообще не очень люблю упорядоченное течение жизни. Эти пять рабочих дней, два выходных, отпуска по расписанию… По темпераменту я спринтер, не стайер. Если загораюсь какой-нибудь идеей, то мне проще вложиться в нее полностью, работать по 10 часов в день, без выходных, чтобы получить результат. И чтобы потом наступила расслабуха – несколько дней, лучше недель ;) полного безделья.

Да, именно такие крайности мне нравятся больше всего: сначала полная концентрация на том, что тебя занимает. Затем полное отключение от всего.

В реальности же есть стандартная рабочая неделя. Проекты, которые нужно сделать (упор на слово “нужно”). Семья, которая не хочет видеть, как я прячусь от ее за письменным столом… Это с одной стороны. С другой стороны есть очередная идея, которая точит мой мозг. Есть желание закрыться где-нибудь часа на три-четыре в одиночестве, с карандашом, бумагой и большой мусорной корзиной. Чтобы раскрутить эту идею с нескольких сторон. Чтобы устать от этого. Чтобы остановиться только тогда, когда я почувствую, что вычерпал тему почти до дна, но только почти, что на донышке еще осталось несколько нетронутых капель. Капель, над которыми затем поработает подсознание…

Но возможности на три-четыре часа отрешиться от окружающего мира нет. Поэтому приходится тратить нервы на то, чтобы отрешится от идеи. Что не очень хорошо получается и это сильно нервирует. Что замето окружающим. Что напрягает меня еще больше, т.к. я понимаю, что тем самым обкрадываю их. Что вытекает в рычание, недовольство, напряжение…

Интересно, изменилась бы ситуация, если бы я занимался более признанной в качестве “творческой” работой? Скажем, был бы писателем или художником. Мог бы я объявить: “У меня вдохновение, я должен зафиксировать этот миг, пока он не исчез”, и закрыться на полдня в мастерской? Возможно, мог бы, поскольку художник может показать дочке фрагмент картины и сказать: “Вот на эту картину я убил сегодняшнее утро. Тебе нравится?”

Про программы такого не скажешь… :(

О фильме “Миллионер из трущоб”

Посмотрел этот фильм находясь под впечатлением рекомендации Гоблина. У меня сложилось несколько негативное отношение к фильму: по мне это голливудская сказка с хэппи-эндом, густо разбавленная “социальной чернухой” для контраста.

Хотя, нужно отдать должное фильму. Опять порадовался, что повезло родится в СССР. И, по крайней мере, в Европейской части СССР не было и нет показанного в фильме ужаса, который до сих пор творится в Индии.

О цифровой фотографии

Цифровой фотоаппарат – это вещь! В течении сорока минут фотографировал увядшую розу. В четыре приема. Сделаю кадров десять-пятнадцать и за компьютер, смотреть, что получилось. Потом еще кадров десять, чтобы снять чуть по другому. И опять посмотреть. И опять переснять. Чтобы выбросить 59-ять неудачных кадров и оставить один, который понравился больше всего:

From Цветы

С пленкой для меня такое было недоступно в принципе!