вторник, 1 января 2030 г.

О блоге

Более двадцати лет я занимался разработкой ПО, в основном как программист и тим-лид, а в 2012-2014гг как руководитель департамента разработки и внедрения ПО в компании Интервэйл (подробнее на LinkedIn). Поэтому в моем блоге много заметок о работе, в частности о программировании и компьютерах, а так же об управлении.

Так же я пишу о жизни вообще и о нескольких своих увлечениях: о фотографии (включая публикацию своих фотографий, некоторые есть и на ZeissImages), о спорте, особенно о дартсе, и, совсем коротко, о кино.

понедельник, 31 декабря 2029 г.

[life.photo] Характерный портрет: вы и ваш мир моими глазами. Безвозмездно :)

Вы художник? Бармен или музыкант? Или, может быть, коллекционер? Плотник или столяр? Кузнец или слесарь? Владеете маленьким магазинчиком или управляете большим производством? Реставрируете старинные часы или просто починяете примус? Всю жизнь занимаетесь своим любимым делом и хотели бы иметь фото на память?

Предлагаю сделать портрет в обстановке, связанной с вашей работой или увлечением. Абсолютно бесплатно. Очень уж мне нравится фотографировать людей в их естественной среде. Происходить это может так...

четверг, 25 августа 2016 г.

[prog.c++.flame] Обновим старый CodeSize Battle: Just::Thread Pro vs SObjectizer-5.5.17

Чуть больше года назад появился пост "Очередной CodeSize Battle: Just::Thread Pro vs SObjectizer-5.5.6". Поскольку версию 5.5.6 уже можно считать довольно старой, то имеет смысл привести для сравнения код, адаптированный под актуальный SO-5. Код для Just::Thread Pro не менялся, т.к. я не помню, чтобы Энтони Уильямс обновлял версию своего фреймворка.

Для тех, кто не хочет заглядывать в старый пост поясню, что речь идет от классической задаче с парикмахером, который спит, пока нет посетителей и просыпается, когда посетители появляются. Под катом показаны реализации на Just::Thread Pro и на SO-5.5.17.

Примечание. Реализация на SO-5 не 1-в-1, но очень близко соответствует версии Уильямса. Дабы не писать постоянно so_5::send(m_log,something) и не колупаться с обнулением содержимого std::ostringstream (что довольно часто происходит в версии Уильямса), в SObjectizer-овской версии было переопределено несколько вариантов operator<<=. Но, т.к. определение этих вспомогательных штук в коде присутствует и объем кода несколько раздувает, то это не такое уж и большое читтерство :)

вторник, 23 августа 2016 г.

[prog.c++] Трансформация маленького класса от совсем простого к легкому хардкору с policy-based design

Upd. Расширенная версия этого текста выложена в виде статьи на Хабре.

Дублирование кода не есть хорошо. Поэтому, когда в коде стали часто повторяться очень похожие фрагменты вида:

class dispatcher_t
   {
      ...
      void
      work_started()
         {
            std::lock_guard< activity_traits::lock_t > lock{ m_stats_lock };

            m_is_in_working = true;
            m_work_started_at = so_5::stats::clock_type_t::now();
            m_work_activity.m_count += 1;
         }

      void
      work_finished()
         {
            std::lock_guard< activity_traits::lock_t > lock{ m_stats_lock };

            m_is_in_working = false;
            so_5::stats::details::update_stats_from_current_time(
                  m_work_activity,
                  m_work_started_at );
         }

      so_5::stats::activity_stats_t
      take_work_stats()
         {
            so_5::stats::activity_stats_t result;
            bool is_in_working{ false };
            so_5::stats::clock_type_t::time_point work_started_at;

            {
               std::lock_guard< activity_traits::lock_t > lock{ m_stats_lock };

               result = m_work_activity;
               iftrue == (is_in_working = m_is_in_working) )
                  work_started_at = m_work_started_at;
            }

            if( is_in_working )
               so_5::stats::details::update_stats_from_current_time(
                     result,
                     work_started_at );

            return result;
         }
   };

То захотелось вынести все это дело в отдельный вспомогательный класс. С очень простой реализацией:

понедельник, 15 августа 2016 г.

[prog.thoughts] Как с философической точки зрения лучше снимать статистику активностей?

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

Создавать историю всех ожиданий не вариант. Т.к. даже за период наблюдения в 1 секунду таких событий может произойти несколько тысяч. Поэтому появилась идея фиксировать статистику по двум временным периодам: по длинному периоду (т.е. от самого начала наблюдений и до текущего момента) и по короткому периоду определенной длины (скажем, в 2 секунды, в этом случае статистика описывает события на глубину в 2 секунды от текущего момента). В статистику входят такие значения, как общее количество событий, суммарная их длительность, минимальные и максимальные значения.

Допустим, мы с толкнулись с таким профилем:

Актор спал от момента t0 до 0.9s, затем с 0.9s до 1.3s обрабатывал сообщение, затем опять заснул до 2.3s, обрабатывал сообщение до 2.7s, спал до 2.9s, обрабатывал сообщение до 3.3s, затем заснул.

Приходит время сформировать статистику за первые 2 секунды, ситуация простая: для длинного и для короткого периодов статистика будет одинаковой. Всего два случая ожидания, суммарное время ожидания 1.6s, минимальное время ожидания на этом отрезке 0.7s (с момента времени 1.3s до 2.0s), максимальное время ожидания -- 0.9s (с момента t0 до 0.9s).

Гораздо интереснее на временной отсечке 4.0s. Со статистикой для длинного периода просто: всего четыре ожидания (0.0s-0.9s, 1.3s-2.3s, 2.7s-2.9s, 3.3s-4.0s), суммарное время ожидания 2.8s, минимум 0.2s, максимум 0.9s. А вот статистику за короткий период (т.е. за интервал 2.0s-4.0s) можно подсчитать двумя способами:

  1. Учитывать факт того, что ожидание началось в предыдущем коротком интервале, на точке 1.3s. В этом случае статистика будет такой: всего три ожидания (1.3s-2.3s, 2.7s-2.9s, 3.3s-4.0s), суммарное время ожидания 1.9s, минимальное 0.2s, максимальное 1.0s.
  2. Не учитывать факт начала ожидания в точке 1.3s, а вести отсчет только от точки 2.0s. В этом случае статистика будет такой: всего три ожидания (2.0s-2.3s, 2.7s-2.9s, 3.3s-4.0s), суммарное время ожидания 1.2s, минимальное время 0.2s, максимальное 0.7s.

Но еще серьезнее эти два подхода скажутся на статистике, которая будет формироваться в точке 6.0s. Для длинного периода опять все просто: всего пять ожиданий, суммарное время ожидания 4.8s, минимум 0.2s, максимум 2.7s. А вот по короткому периоду статистика будет такой:

  1. Если учитывать факт начала ожидания в точке 3.3s, то одно ожидание суммарной длительностью 2.7s, минимум и максимум по 2.7s;
  2. Если вести отсчет только от точки 4.0s, то одно ожидание суммарной длительностью 2.0s, минимум и максимум по 2.0s.

И вот вопрос, на который у меня нет ответа: какой из двух подходов к формированию статистики за короткий интервал считать правильным?

На мой взгляд, первый подход, когда мы учитываем факт начала ожидания в предыдущем периоде, более адекватный. Ведь, когда мы в точке 6.0s подводим итог наблюдений за последние 2 секунды, то у нас действительно всего один период ожидания, которое длится уже больше 2-х секунд. И то, что это единственное ожидание по времени больше, чем период наблюдения, для нас не суть важно.

С другой стороны, такие результаты могут быть обескураживающими для наблюдателя. Т.е. увидев, что на отрезке в 2 секунды актор провел в ожидании 2.7s невольно задаешься вопросом: а как такое вообще возможно? И нужно приложить некоторые усилия, чтобы понять что к чему.

Еще хуже первый подход для сценариев, когда мы пытаемся использовать статистику по коротким периодам для визуализации нагрузки на актора. Допустим, мы хотим рисовать график, на котором отображается процент загрузки актора. И обновляем этот график раз в две секунды. Если у нас суммарное время за короткий интервал может превышать длину интервала, рисование графика будет чуть сложнее. А вот во втором подходе, в котором такой ситуации быть не может, все гораздо проще. Плюс к тому, во втором подходе у нас появляется возможность просто суммировать показания статистики за короткие периоды и получать результаты, согласованные со статистикой по длинному периоду. Что так же упрощает обработку собираемой статистики.

Собственно, интересно мнение читателей -- а вы какой из подходов считаете правильным? Или может я вообще не прав, пытаясь считать статистику и по длинному, и по короткому периодам. И общепринятым считается какой-то другой способ?

суббота, 13 августа 2016 г.

[prog.c++] И еще раз шаблоны, шаблоны, кругом одни шаблоны :)

Небольшая выжимка "из недавнего":

templatetypename DEMAND_QUEUE >
struct common_data_t {...};

class no_activity_tracking_impl_t
   : protected common_data_t< demand_queue_no_activity_tracking_t > {...};

class activity_tracking_impl_t
   : protected common_data_t< demand_queue_with_activity_tracking_t > {...};

templatetypename IMPL >
class work_thread_template_t : public IMPL {...}

using work_thread_no_activity_tracking_t =
   work_thread_template_t< no_activity_tracking_impl_t >;

using work_thread_with_activity_tracking_t =
   work_thread_template_t< activity_tracking_impl_t >;

Тут и шаблоны, и наследование реализации, и даже CRTP... Боюсь, если придется объяснить что к чему, то в двух словах это и не получится :)

Все эти навороты потребовались, чтобы переделать старый код, который был написан лишь для одного конкретного сценария работы. Потребовалось сделать так, чтобы в одной ситуации он работал одним образом, а в другой -- другим. При этом для второй ситуации нужно было внутрь класса добавить и дополнительных атрибутов, и дополнительных методов. Но изрядная часть функциональности work_thread для обоих ситуаций должна была оставаться одинаковой. Благодаря продвинутым возможностям C++ этого удалось достичь без копипасты.

четверг, 4 августа 2016 г.

[prog] Демонстрация поведения разных ObjPlacement в MxxRu

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

Итак, пусть у нас есть вот такая файловая структура:

.
├── build.rb
└── demo
    ├── prj.rb
    └── src
        └── some_rather_long_path
            └── to_source
                └── file
                    └── main.cpp

Где main.cpp -- это классический hello_world:

#include <iostream>

int main() {
   std::cout << "Hello, world" << std::endl;
}

Проектный файл для этого hello_world имеет простейший вид (файл demo/prj.rb):

gem 'Mxx_ru'
require 'mxx_ru/cpp'

MxxRu::Cpp::exe_target {
  target 'demo.app'
  cpp_source 'src/some_rather_long_path/to_source/file/main.cpp'
}

Ну и в build.rb пока ничего интересного нет вообще:

#!/usr/bin/ruby
gem 'Mxx_ru''>= 1.6.12'
require 'mxx_ru/cpp'

MxxRu::Cpp::composite_target( MxxRu::BUILD_ROOT ) {
  required_prj 'demo/prj.rb'
}

Запускаем сборку ./build.rb --mxx-cpp-release и получаем следующее содержимое: