Как стать автором
Обновить
71
Карма
0
Рейтинг
Евгений Охотников @eao197

Проджект-лид

  • Подписчики 62
  • Подписки 1

Один день из жизни разработчика PVS-Studio, или как я отлаживал диагностику, оказавшуюся внимательнее трёх программистов

Понятно, спасибо за ответ.


sizeof(a.consoleText) — textSize > 5

Какой-нибудь gcc на высоких уровнях предупреждений ругнется на сравнение unsigned с signed. Уж извините за занудство :)


Кроме того, тут напрашивается еще вот какой момент: этот вариант можно считать надежным только в случае, если гарантируется, что в a.consoleText будет 0-символ. Если же разработчик где-то со значением consoleText ошибся и 0-символ содержится в sizeof(a.consoleText)+1u или sizeof(a.consoleText)+2, то (sizeof(a.consoleText) — textSize) приведет к переходу через ноль.


Но стоит ли этим заморачиваться, ведь отсутствие 0-символа в a.consoleText — это явно ошибочная ситуация, которая должна предотвращаться другими способами? У меня нет однозначного ответа на этот вопрос.

Один день из жизни разработчика PVS-Studio, или как я отлаживал диагностику, оказавшуюся внимательнее трёх программистов

Ваше мнение понятно. Позволю, однако, напомнить, что свой вопрос я задавал не вам, а разработчику PVS-Studio чтобы получить ответ из первых рук.

Один день из жизни разработчика PVS-Studio, или как я отлаживал диагностику, оказавшуюся внимательнее трёх программистов

Класс std::string для строчек в ~500 байт будет использовать динамическую память, а это может быть нежелательно по тем или иным причинам. Кроме того, могут быть причины для того, чтобы оставить код в его исходном виде, не переписывая на модный и современный стандарт C++.

Один день из жизни разработчика PVS-Studio, или как я отлаживал диагностику, оказавшуюся внимательнее трёх программистов

А как должен быть исправлен проблемный фрагмент чтобы PVS-Studio перестал диагностировать ошибку? Перед вызовом strncat нужно сделать проверку длины строки? Типа:


if(strlen(a.consoleText)+5u < sizeof(a.consoleText))
{
   strncat(a.consoleText, inputBuffer, sizeof(a.consoleText) –
                                      strlen(a.consoleText) - 5);
}

Худшие места в C++ для написания кода

У меня нет возражений. У меня есть неприятие вашего подхода и ваших взглядов на то, как C++ должен использоваться. И это касается не только шаблонов, и вообще всего, что описано в статье. Включая отдельные примеры кода.

Худшие места в C++ для написания кода

Скажите пожалуйста, вашу статью следует воспринимать всерьез? Выглядит она как своеобразная сатира. Вот и вынужден задать столь тупой вопрос, поскольку не могу понять, как же к ней относиться.

Развитие проекта arataga: пара рефакторингов по результатам натурных испытаний

В одно время генерировались заявки на запуск хэндлеров. Далее уже хэндлеры на каждой io_threads запускались последовательно.


Соответственно, накладные расходы складываются из двух составляющих:


  1. Единомоментная генерация заявок. Если нам нужно сгенерировать их 15K, то это явно будет дороже, чем если нам их нужно сгенерировать 8 (восемь) штук.
  2. Суммарное время запуска всех хэндлеров, даже тех, которым прямо сейчас нечего делать.

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


Вашу идею можно было бы развить следующим образом: завести не один mbox для one_second_timer, а N. И заставлять acl_handler-ов подписываться на один из них (случайным образом или посредством какого-то распределения). Для каждого из этих N mbox-ов запускается свой экземпляр one_second_timer. Тогда при наступлении очередного таймерного события единовременная отсылка для каждого из этих N mbox-ов занимала бы гораздо меньше времени.


Как развитие этой идеи можно было бы сделать свой кастомный mbox, который бы сам шардировал подписки. И когда таймер отдает этому mbox-у очередной one_second_timer, то сам mbox отдает one_second_timer сперва первой шарде, затем второй, затем третьей и т.д. Основой для шардирования могли бы стать идентификаторы рабочих нитей, с которых выполняется подписка.


Однако, ключевой момент в том, что если у нас из 15K acl_handler-ов в таймере сейчас заинтересованы только 5K, то оставшиеся 10K вообще никак нельзя трогать.

Приёмы неблокирующего программирования: атомарные операции и частичные барьеры памяти

Оригинальная статья читается легче и оказывается понятнее, чем ваш перевод. В частности, вот момент, который в вашем переводе вызвал недоумение и заставил заглянуть в оригинал:


Пусть, например, у вас есть struct work_struct, которые в фоне затирают ненужные массивы единичками. После запуска задачи у вас есть другие важные дела и массив вам не нужен.

Что за ерунда? Тогда как в оригинале все складно и очевидно:


Suppose, for example, that you have multiple work items that fill certain elements of an array with ones; whoever spawned the work items will only read the array after calling flush_work().

Обзор последних изменений в rotor'е (v0.10… v0.14)

Вы в рассказе про нововведения в rotor так часто упоминаете sobjectizer, что мне даже несколько неловко становится. Ну и раз такое дело то, что что хочется сказать: в разработку и популяризацию sobjectizer-а было вложено очень много сил и времени, но результат, как показывает жизнь, оказывается ну так себе. Волей не волей, но в голову закрадываются мысли вроде "а стоило ли но все того?"


А вот читаешь вашу статью, осознаешь, что наши работы над sobjectizer-ом дали вам хоть какую-то пищу для размышлений, и… И вроде бы получается, что не зря. Для кого-то наша работа оказалась полезной.


Так что желаю вам удачи с развитием и, особенно, с продвижением rotor-а. Пусть вам повезет больше. И пусть ваш проект окажется успешнее и востребованнее, чем наш.

Я единственный из 1400, или самый крутой рекрутинг, что я проходил

Это дорого должно быть независимо от размера.

Дорого/дешево — это же относительные величины. Скажем, $5 за обед — это дорого или дешево? Ответ на этот вопрос будет разным для человека с ЗП в $300 и в $3000 в месяц.


Так же и здесь. Если на проекте работает всего 2 разработчика и один из них тратит на собеседования по 5 часов в неделю, то вклад в накладные расходы на стоимость работ одни. Если же на проекте работает 20 разработчиков и один из них тратит по 10 часов в неделю на собеседования, то вклад в накладные расходы будет уже другим. Скорее всего существенно ниже.

Я единственный из 1400, или самый крутой рекрутинг, что я проходил

То, что это какая-то маленькая компания было понятно по тому, как автор статьи несколько раз сказал, насколько им дорого проводить этапы отбора и найма.

Как и почему я перестал покупать новые ноутбуки

lenovo thinkpad e330, качество сборки очень хорошее, клавиатура отличная, не знаю, как у автора на схожей модели вылетают клавиши, это странно.

У меня в качестве ноутбука "для поездок" лежит lenovo thinkpad e130, купленный в 2012 за приблизительно такие же деньги. С одной стороны, неубиваемый, даже пролитый на него чай пережил без проблем. Но вот одна из клавиш на клавиатуре отлетела году в 2014-ом.

Не хочется ждать в очереди? Напишем свой диспетчер для SObjectizer с приоритетной доставкой

Чисто акторное тоже техническое решение, которое напрашивается, это актор-сортировщик, к которому стекаются все заявки, а он уже внутри себя раздаёт их на обработку

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


Собственно, это одна из причин, по которой изначально в SO-5 поддержки приоритетов вообще не было.


Но collector-performer — это все-таки два агента. Требует больше работы и сопровождать такой код может быть сложнее, особенно когда на проект приходят новые люди и им нужно разобраться в разделении логики между агентами.

Не хочется ждать в очереди? Напишем свой диспетчер для SObjectizer с приоритетной доставкой

Насколько это корретно в общем случае?

Приоритетная обработка и общий случай — это вообще плохосовместимые понятия. Как я говорил, в SO-4 была поддержка приоритетов, но на практике пользы от нее не было. За исключением, может быть, одного или двух случаев.


Так что, как только возникают приоритеты, то мы практически сразу начинаем говорить о каких-то специальных ситуациях. ИМХО.


Есть ли пример, когда это не выполняется, т.е. когда msg_progress всё ещё важны, даже после получения msg_result?

В статье приведен вырожденный случай, который должен быть максимально понятный.


На практике для status и result, скорее всего, первый же result должен был бы сделать неактуальными все предшествующие status-ы. И нам бы, скорее всего, потребовалась бы очередь, которая бы не только ставила бы result на первое место, но и выбрасывала бы предыдущие status-ы, которые уже были в ней.


Но на той же практике ситуация могла бы быть и посложнее. Например, агент, который ждет result и status, инициирует не одну операцию, а 100500. И на каждую из начатых операций он ждет result-ы и status-ы. А сопоставляет пришедшие сообщения с ранее начатыми запросами по каким-то ID внутри сообщений. Тогда в очереди может стоять 900 status-ов для запросов с разными ID и поступление единственного result-а инвалидирует только чать этих статусов.


Решаться это может тем, что агент держит у себя словарь начатых запросов и при получении status-а проверяет наличие записи в этом словаре. Если записи нет, то либо result уже был получен. И status можно легко проигнорировать.


Если же говорить о применении приоритетов на практике, то могут быть и ситуации, которые не связаны со status/result вообще. Скажем, есть агент, который обрабатывает поток заявок. Часть этих заявок идут от клиентов, которые прямо сейчас находятся в он-лайне, а часть заявок выдается какой-то системой по расписанию. Поскольку мы хотим, чтобы клиенты в он-лайне обслуживались максимально быстро, мы можем поднять приоритет для тех сообщений, которые касаются заявок этих клиентов. Тогда как приоритет для сообщений, касающихся заявок от автоматизированной системы будет понижен.

Конечные автоматы на страже порядка

А статью вы писали для членов команды или для широкого круга?


Читателям, которые мало знают про КА вы портите картину мира.
У читателей, которые плотно работают с КА и используют state chart-ы ваши диаграммы вызывают недоумение, мягко говоря.

Андрей Терехов: от Фортрана до Питона

Уже есть такой язык — Ди, его придумал Андрей Александреску.

D придумал не Александреску, а Вальтер Брайт, у которого за плечами разработка компиляторов Zortech C++ и Digital Mars C++. Александреску к разработке D подключился лет через 6-7 после начала работ над D, году в 2005-ом или 2006-ом.

Конечные автоматы на страже порядка

В разговоре про КА ожидаешь увидеть state diagrams, а не dataflow diagrams: https://en.wikipedia.org/wiki/State_diagram#State_diagrams_versus_flowcharts
Иллюстрировать же КА блок-схемами… Ну такое себе.

Новая функциональность в RESTinio и опять с помощью C++ных шаблонов

Так это обратная сторона гибкости.


Если нам плевать на мелкие расходы, мы можем пойти по обычному ОО-пути, т.е. абстрактный базовый тип abstract_ip_blocker_t, от него наследники, которые делают то, что нужно.


Если нем не плевать и мы хотим, чтобы никакого ip_blocker-а вообще не было и на работу с ним даже минимальные ресурсы не тратились, то тогда только шаблоны остаются.


RESTinio позволяет идти любым из этих путей.


И это касается не только ip_blocker-а. С логированием такая же ситуация.

Новая функциональность в RESTinio и опять с помощью C++ных шаблонов

Самый простой вариант.


Для ограничения общего количества подключений. В свойствах жестко задаете use_connection_count_limiter=true. Если в конфиге задано не ограничивать общее число подключений то либо не вызываете max_parallel_connections для settings, либо вызываете max_parallel_connections с заведомо большим значением (например, std::numeric_limits<std::size_t>::max()). Если в конфиге задано ограничивать общее количество, то вызываете max_parallel_connections с заданным в конфиге значением.


Для ограничения числа подключений с одного IP. Делаете свой IP-blocker (по типу вот такого) и указываете его тип в traits для сервера. При создании IP-blocker-а задаете в нем ограничение из конфига (опять же std::numeric_limits<std::size_t>::max() можно использовать для случая, когда ограничения нет).


Либо для IP-blocker-а можете описать у себя базовый тип:


class abstract_ip_blocker {
public:
   virtual ~abstract_ip_blocker();

   virtual restinio::ip_blocker::inspection_result_t
   inspect(const restinio::ip_blocker::incoming_info_t & info ) noexcept = 0;

   virtual void
   state_changed(const restinio::connection_state::notice_t & notice) = 0;
};

В traits сервера в качестве ip_blocker_t задаете abstract_ip_blocker.


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


Если же идти сложным путем, то потребуется несколько разных типов traits:


struct no_limits_traits : public restinio::default_traits_t {};

struct total_connections_limit_only_traits : public restinio::default_traits_t {
   static constexpr bool use_connection_count_limiter = true;
};

struct ip_blocker_only_traits : public restinio::default_traits_t {
   using ip_blocker_t = some_my_actual_ip_blocker_type;
};

struct all_limits_traits : public restinio::default_traits_t {
   static constexpr bool use_connection_count_limiter = true;
   using ip_blocker_t = some_my_actual_ip_blocker_type;
};

И в программе, в зависимости от того, что задано в конфиге, создаете сервер на базе разных traits.

Новая функциональность в RESTinio и опять с помощью C++ных шаблонов

Так пока вроде все логично развивается:


  • возникает вопрос "А как ограничить количество подключений с одного IP?"
  • мы понимаем, что пока никак;
  • спустя время мы делаем возможность ограничить количество подключений с одного IP;
  • возникает вопрос "А как ограничить общее количество подключений?"
  • мы понимаем, что пока никак;
  • спустя время мы делаем возможность ограничить общее количество подключений.

Возникнет следующий вопрос — будем смотреть на местности что к чему. Удобство же можно оценивать только по отношению к тому, что есть. Не так ли?

Информация

В рейтинге
4,268-й
Откуда
Гомель, Гомельская обл., Беларусь
Зарегистрирован
Активность