вторник, 23 сентября 2014 г.

[prog.c++] timertt: обслужить миллиард таймеров? Да легко!

По следам обсуждения новости о библиотеке timertt на LOR-е. В обсуждении добрый человек предложил код теста, в котором запускается на обслуживание не много, ни мало, один миллиард таймеров (несколько раз нолики в числе пересчитывал, вроде девять):

#include <iostream>
#include <cstdlib>

#include "timertt/all.hpp"

using namespace std;
using namespace std::chrono;
using namespace timertt;


int main()
{
   timer_wheel_thread_t tt;

   // Timer thread must be started before activation of timers.
   tt.start();
   
   int counter = 0;
   forint i = 0 ; i < 1000000000 ; ++i )
   tt.activate( milliseconds( 100 ),
         [&counter](){ ++counter; } );

   while( counter != 1000000000 )
      this_thread::sleep_for( chrono::milliseconds( 100 ) );
   
   cout << counter << endl;
}

С некоторым предательским чувством в коленках скомпилировал и запустил (MSVC++2013 64-bit, Win 8.1 64-bit):

bash-3.1$ time ./many_single_shot_timers.exe
1000000000

real    4m59.429s
user    0m0.000s
sys     0m0.015s

Таки миллиард таймеров за 5 минут, т.е. по 200 миллионов в минуту, т.е. по 3.3(3) миллиона в секунду. С учетом того, что профилированием и оптимизацией я не занимался, то получается вполне себе достойно. Честно скажу, вообще на таких объемах не запускал, не был уверен, что... :)))

Upd. Этот же тест, но с механизмом timer_list -- 4m31s, расход памяти приблизительно такой же -- около 42Mb. А вот timer_heap подкачал: 28m56s при расходе памяти под гигабайт. Вполне ожидаемый вывод: timer_heap не подходит для очень большого количества таймеров. Только удивительно, насколько именно не подходит.

PS. ЛОР -- торт! ;)

[prog.c++] libcds обновился до версии 1.6.0

Состоялся релиз библиотеки CDS (Concurrent Data Structure) 1.6.0. Хорошая C++ная библиотека с большой кучей всякого разного для lock-free под вменяемой лицензией. Если нужно что-то C++ное из этой области, то имеет смысл начинать поиски именно отсюда. Тем более, что разработчик из России, с ним можно общаться на русском языке.

[prog.memories] Vim, Ruby, Mxx_ru -- десять лет в пути...

Проработать на одном месте или над одним проектом долгое время -- это, блин, уникальный опыт. Причем долгое -- это где-то около 10 лет. Такой срок позволяет разглядывать некоторые вещи в действительно исторической перспективе. То, что когда-то нравилось и казалось удачным решением, с течением времени оказалось выброшенной пустышкой или же стало непозволительно дорогим при сопровождении. И наоборот, какая-то мелочь, которая была придумана сходу и которой не придавали особого значения, выдержала проверку временем и стала одной из немногих вещей, за которые реально не стыдно. А потом все поменялось :) Происходит что-то, что переводит старую разработку из дорогой в сопровождении во вполне себе хороший инструмент, который востребован, уже адаптирован под текущие нужды и не так уж много времени и сил требует на поддержание своей работоспособности. Тогда как вещь, которая верой и правдой служила тебе 7-8 лет подряд, и которой ты долгое время гордился, потихонечку отправляется куда-то на задворки в ожидании очередного поворота истории :)

понедельник, 22 сентября 2014 г.

[prog] Несколько вопросов к читателям

Вот уже несколько дней как удав по стекловате тащусь от возможности использовать ядро SObjectizer без ACE Framework. Вечный кайф, как говорится :) Перекомпиляция гораздо шустрее. Хошь Visual-ом компилируешься, хошь MinGW, хошь Cygwin-ом, хошь clang-ом -- лепота!

В связи с этим первый вопрос: кто-нибудь устанавливал себе clang под Windows, но с libc++ (инсталлятор под Windows, который лежит на llvm.org идет без libc++)? Под ArchLinux у меня clang есть, хочется еще и под Windows чтобы родной был.

Второй вопрос связан с Git-ом. Вместе с Svn сразу шла отличная Subversion Book, в которой все очень подробно и здорово объяснялось буквально на пальцах (хотя, как показала практика, мало кто ее реально читал). А есть ли что-то подобное для Git? Или в более общем смысле: что порекомендуете прочитать по Git-у, чтобы лаконичное и толковое и чтобы можно было быстро начать Git использовать?

Третий вопрос так же связан с Git-ом. Какую версию Git-а порекомендуете использовать под Windows? Нужна простая консольная версия. Без интеграций с Windows Shell и какой-либо IDE. Вроде как пару лет назад с портами Git под Windows было не очень гладко, потому и спрашиваю.

Git мне нужен вот для чего. Mxx_ru всегда жил на RubyForge. Но с прошлого года этот сайт взял и закрылся. Svn-репозиторий еще работает, но в моем случае только на чтение, т.к. после переезда с одной машины на другую, мои SSH-ные ключи устарели. А обновить их без работающего RubyForge не представляется возможным. Соответственно, чтобы проапдейтить Mxx_ru нужно его куда-то перенести. Первое, что приходит в голову -- это GitHub. Но, т.к. о Git-е у меня только поверхностные впечатления, то сначала нужно с ним познакомиться поближе.

воскресенье, 21 сентября 2014 г.

[prog.c++] Предопределенные макросы компилятора

Наткнулся в Интернете на несколько статей, посвященных предопределенным макросам С++ компиляторов. Интересно и полезно:

C/C++ tip: How to list compiler predefined macros. Наборы аргументов командной строки для нескольких компиляторов, которые заставляют компилятор выдать список предопределенных макросов.

C/C++ tip: How to detect the operating system type using compiler predefined macros. Макросы компилятора, которые позволяют определить ОС.

C/C++ tip: How to detect the processor type using compiler predefined macros. Макросы компилятора, которые позволяют определить тип процессора.

Pre-defined Compiler Macros. Проект на SourceForge, который собирает информацию, аналогичную трем приведенным выше статьям.

Так же отмечу раздел Articles на ресурсе, где размещены три первые статьи. Имхо, довольно интересная информация там представлена.

суббота, 20 сентября 2014 г.

[prog.c++] Тему с самодельными spinlock-ами закрываю. Не осилил

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

Поэтому эксперименты с реализацией spinlock-ов на чистом C++11 без привлечения внешних библиотек заканчиваю. Все, что удалось сделать, находиться вот в этой ветке Svn-репозитория. Лицензия у этого кода BSD-шная. Если кто-то хочет поковырять палочкой кучку -- you are welcome :) В принципе, я не против развить это во что-то нормальное, но только если этим будет заниматься кто-то с лучшим, чем у меня, пониманием предметной области. Меня хватит разве что на написание кода, дилетантские вопросы и провинциальную критику ;)

Что сейчас есть в spinlockspp? Есть самый простой спинлок, под названием trivial-lock, работающий на основе механизма test-test-and-set.

Есть очень тривиальная, но весьма медленная, реализация ticket-lock-а. В ней два атомарных счетчика. В то время как классическая реализация ticket-lock-а использует union, в котором лежат, скажем, одна 32-битовая переменная и структура из двух 16-битовых. Либо же просто одна 32-х битовая переменная, но для операции unlock задействуются только 16 бит из нее (т.е. применяется атомарный инкремент как будто к независимой 16-битовой переменной). Оказалось, что стандартными средствами С++11 этот фокус просто так не проделать. Поскольку std::atomic_fetch_add принимает в качестве аргумента не адрес ячейки памяти, а указатель на объект std::atomic. Тогда как C-шная функция atomic_fetch_add получает именно что адрес. И в C-шную fetch_add засунуть адрес части счетчика из ticket-lock-а несколько проще, чем в C++ (а в C++ прибегать к грязным хакам стремно, если честно). Чтобы задействовать C-шную fetch_add в C++ном коде, нужно, чтобы компилятор поддерживал и C++11, и C11. Но тот же Visual C++ 2013, например, не поддерживает C11. Поэтому ticket-lock такой примитивный.

Зато ticket-lock, в отличии от trivial-lock, является "честным".

Есть очень шустрая, но "нечестная" реализая RW-lock-а под названием dvyukov_rw_lock, разработанная Димой Вьюковым для проекта LLVM.

Отдельный вопрос -- это организация паузы в спинлоках, для случая, когда установить блокировку не удалось. Этот вопрос решается backoff-ами. Каждый спинлок реализуется шаблонным классом, параметризуемым конкретным типом backoff-а. В spinlockspp есть два платформенно независимых типа backoff-а. Это yield_backoff, который вызывает std::thread::yield(). И simple_inc_backoff_t, который использует простой цикл с инкрементом целочисленной переменной. Верхняя граница этого цикла постоянно увеличивается, так что при каждом новом ожидании пауза удлиняется.

Есть еще платформенно зависимый backoff под названием cpu_pause_backoff_t. Который использует инструкцию PAUSE для процессора. Правда, поскольку C++ не имеет стандартных средств выдачи таких инструкций, пришлось мудрить с #if/#else/#endif. С помощью онлайн компиляторов удалось получить вот такую штуку. Вроде под Visual C++/GCC/Clang/ICC должно работать. Понятное дело, что при переходе на другую платформу или другой компилятор, нужно будет делать новую реализацию SPINLOCKSPP_CPU_PAUSE.

Собственно, откуда пошли spinlock-и. От желания отказаться от использования ACE Framework и перейти только на стандартную библиотеку C++11. Но нужны были RW-мутексы, которых в C++11 нет. Зато есть средства работы с atomic-ами, поэтому появилась мысль наваять замену ACE_RW_Thread_Mutex на C++ных atomic-ах. В простой форме это было сделано и использовано.

А вот попытка сделать что-то более солидное закончилось пониманием, что одними только средствами C++11 обойтись не получится. А именно в этом и был интерес: оставаться только в рамках стандартной библиотеки и не адаптировать свой код под каждую платформу.

Но одним только C++11 не обойтись. Нужно глубоко погружаться в детали платформы и компилятора. Для этого нужно иметь соответствующую квалификацию. Либо желание и время ее приобрести. Чего в моем случае нет. Посему работы прекращаю.

Сухой остаток. Используйте спинлоки только в случае, когда точно уверены и имеете объективные доказательства профайлера о том, что производительности std::mutex-а явно недостаточно. При этом выполняемые под спинлоком действия должны быть очень короткими. И, кроме того, борющиеся за ресурсы нити должны быть распределены по разным ядрам процессора. Если же ядер меньше, чем потоков, то пользы от спинлоков может и не быть. Более того, можно еще и круто проиграть.

Ну а если спинлоки таки нужны, то лучше не делать их самому. А поискать что-нибудь готовое. Насколько много такого готового -- фиг знает. Для чистого C мне понравился код библиотеки Concurrency Kit. Есть ли что-то подобное для C++, да еще не под GPL лицензией -- это вопрос. Мне ничего на глаза не попалось. Разве что в составе каких-то больших библиотек/проектов (как с тем же rw_spinlock-ом от Димы Вьюкова).

пятница, 19 сентября 2014 г.

[prog] Пара ссылок на материалы, посвещеные примитивам синхронизации

Очень интересная статья "Spinlocks and Read-Write Locks" на сайте locklessinc.com. Обсуждается несколько вариантов spinlock-ов и read-write spinlock-ов, с кодом, результатами замеров и последовательным изложением достоинств и недостатков каждого метода.

Большая подборка разнообразных ссылок: Reader/Writer Locking and Beyond. Подборка от 2009-го года, поэтому какие-то ссылки уже наверняка "протухли", но тем не менее, общее их количество внушаить.

PS. Подмывает взяться за реализацию ticketlock (mutex-like spinlock) и rwticket (read-write spinlock) из первой статьи. Дабы включить эти реализации в cppinlocks/cppspins. Но есть ощущение, что рискую ввязаться совсем не в свое дело...