Friday, July 10, 2009

valgrind, dlopen and debug info

Столкнулся сегодня с проблемой, что KCachegrind не отображает отладочную информацию для некоторых модулей.

Покопавшись в интернетах наткнулся на этот баг (valgrind doesn't output debug info with dlopen'ed libraries ) и вспомнил, что модули у нас загружаются динамически.

Как сказано в последнем сообщение это все таки баг:
The bug is still there.
I did some debugging with Julian and we came to the conclusion that the problem is with chroot() after dlopen'ing the library.


Я проверил код, но явно chroot нигде не вызывается.

Полез искать дальше и наткнулся на следующий баг (Debug info is lost for .so files when they are dlclose'd ). Что навело меня на мысль не выгружать модули.

И на самом деле помогло.

Так, что если модули активно не загружаются и не выгружаются для профилирования можно отключить выгрузку модулей. Ну или пофиксить valgrind.

Saturday, March 28, 2009

Domain-Specific Program Generation

А ни у кого случайно нет вот этой книги в электронном виде?

upd:
Уже не надо, помогли товарищи из стенфорда. Сейчас у меня есть статьи из нее:
- Program Optimization in the Domain of High-Performance Parallelism
- A Personal Outlook on Generator Research
- PiLib: A Hosted Language for Pi-Calculus Style Concurrency
- A Language and Tool for Generating Efficient Virtual Machine Interpreters
- Optimizing Sequences of Skeleton Calls
- Domain-Specific Optimizations of Composed Parallel Components
- Runtime Code Generation in C++ as a Foundation for Domain-Specific Optimisation
- Guaranteed Optimization for Domain-Specific Programming

Tuesday, March 24, 2009

ODR violation

Наткнулся сегодня на нарушение ODR (если я правильно понимаю).

Суть в том, что если класс имеет __declspec specifier и, кроме того, имеется forward declaration и у этого forward declaration __declspec specifier не указан, то можно поиметь кучу проблем, например, с экспортом символов в dll - их может в dll просто не оказаться.

Sunday, March 22, 2009

Про систему событий

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

Для начала скажу, что если мы регистриуем события begin, end то такая система позволит писать нам this->on_begin () или this->on_end (), а не this->fire_event (begin_signal). К тому же, мы можем иметь любое количество параметров различных типов и проверка типов будет выполняться на этапе компиляции, т.е. например, this->on_begin (clock ()).

Как это использовать:
      DECLARE_EVENT_LIST (reservoir_simulator,
signal_params_t,
17,
((begin, (clock_t), 1)
, (before_initial_conditions, (), 0)
, (after_initial_conditions, (), 0)
, (begin_large_step, (), 0)
, (before_wells_process, (), 0)
, (before_grp_process, (), 0)
, (after_grp_process, (), 0)
, (before_low_skin_factor, (), 0)
, (after_low_skin_factor, (), 0)
, (after_large_step, (), 0)
, (begin_small_steps, (), 0)
, (after_small_steps, (), 0)
, (before_save_data, (), 0)
, (after_save_data, (), 0)
, (before_history_matching, (), 0)
, (after_history_macthing, (), 0)
, (end, (clock_t), 1)
));
_Winnie C++ Colorizer

Самое интересное здесь это:
                          ((begin, (clock_t), 1)
, (before_initial_conditions, (), 0)
, (after_initial_conditions, (), 0)
, (begin_large_step, (), 0)
, (before_wells_process, (), 0)
, (before_grp_process, (), 0)
, (after_grp_process, (), 0)
, (before_low_skin_factor, (), 0)
, (after_low_skin_factor, (), 0)
, (after_large_step, (), 0)
, (begin_small_steps, (), 0)
, (after_small_steps, (), 0)
, (before_save_data, (), 0)
, (after_save_data, (), 0)
, (before_history_matching, (), 0)
, (after_history_macthing, (), 0)
, (end, (clock_t), 1)
)
_Winnie C++ Colorizer

Это наш список событий - каждое событие описывается кортежом, состоящим из имени события, списка параметров (тоже кортеж) и количества параметров с списке.

Итак:
#define DECLARE_EVENT_LIST(name_, params_, ts_, t_)\
public:\
DECLARE_SIGNALS_ENUM(ts_, t_)\
BOOST_PP_SEQ_FOR_EACH_I(UDECL_I, params_, BOOST_PP_TUPLE_TO_SEQ(ts_, t_))\
EXPORT_SIGNALS_ENUM(name_, ts_, t_)
_Winnie C++ Colorizer


name_ - prefix, params_ - тип параметров, ts_ - количество событий в кортеже, t_ - кортеж событий. Про params_ я объясню позже.

DECLARE_SIGNALS_ENUM(ts_, t_) - из кортежа событий делаем enum.
BOOST_PP_SEQ_FOR_EACH_I(UDECL_I, params_, BOOST_PP_TUPLE_TO_SEQ(ts_, t_)) - создаем набор функций типа on_[event_name].
EXPORT_SIGNALS_ENUM(name_, ts_, t_) - выгрузка этого дела в питон.

Далее:
#define DECLARE_SIGNALS_ENUM(ts_, t_)\
enum signal_codes {\
BOOST_PP_SEQ_FOR_EACH_I(DECLARE_SIGNAL_ENUM_I, _, BOOST_PP_TUPLE_TO_SEQ(ts_, t_))\
};
_Winnie C++ Colorizer


BOOST_PP_TUPLE_TO_SEQ(ts_, t_) - делаем из кортежа событий последовательность.
BOOST_PP_SEQ_FOR_EACH_I(DECLARE_SIGNAL_ENUM_I, _, BOOST_PP_TUPLE_TO_SEQ(ts_, t_)) - для каждого элемента последовательности применяем макрос DECLARE_SIGNAL_ENUM_I.
#define DECLARE_SIGNAL_ENUM_I(r, data, i, elem)\
BOOST_PP_CAT(BOOST_PP_TUPLE_ELEM(3,0,elem), _signal),
_Winnie C++ Colorizer

BOOST_PP_CAT(BOOST_PP_TUPLE_ELEM(3,0,elem), _signal) - т.к. событие у нас описывается кортежом и в данном макросе мы получили его в elem, то надо вытащить из него первые элемент, ну и добавить к нему префикс. На выходе получим нечто типа:
enum {
begin_signal,
end_signal,
};

Теперь нам надо создать функции on_[event_name].
BOOST_PP_SEQ_FOR_EACH_I(UDECL_I, params_, BOOST_PP_TUPLE_TO_SEQ(ts_, t_)) - тоже самое, делаем последовательность и для каждого элемента применяем макрос UDECL_I.
#define DECL(type_, name_, t_, ts_)\
void BOOST_PP_CAT(on_, name_) (PARAM_LIST(BOOST_PP_TUPLE_TO_SEQ(ts_, t_), ts_))\
{\
type_ params;\
PUSH_LIST (BOOST_PP_TUPLE_TO_SEQ(ts_, t_), ts_)\
this->fire_signal (BOOST_PP_CAT(name_, _signal), params);\
}

#define UDECL_I(r, data, i, elem)\
DECL(data, BOOST_PP_TUPLE_ELEM(3,0,elem), BOOST_PP_TUPLE_ELEM(3,1,elem), BOOST_PP_TUPLE_ELEM(3,2,elem))
_Winnie C++ Colorizer

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

PUSH_LIST и PARAM_LIST у меня самописные (т.к. project-specif), но если разобраться как устроены некоторые BOOST_PP то можно подобрать аналог или написать свои, это на самом деле не сложно.

Выгрузка в питон тоже не представляет интереса, т.к. делается по аналогии.

Ну вроде все.

template class overriding

Тут на днях выяснилось, что не все знают про один плюснутый трюк. Как известно, C++ не позволяют иметь шаблонные классы с одинаковым именем, но разным количеством шаблонных параметров. Т.е. код
template <typename X>
struct S
{
};
template <typename X, typename Y>
struct S
{
};
_Winnie C++ Colorizer


не является валидным. Но в некоторых случаях это ограничение можно обойти с помощью перегрузки функций:
template <typename X>
struct S_X
{
};
template <typename X, typename Y>
struct S_XY
{
};
template <typename X>
S_X <X>
S (/*params*/)
{
return S_X <X> (/*params*/);
}
template <typename X, typename Y>
S_XY <X, Y>
S (/*params*/)
{
return S_XY <X, Y> (/*params*/);
}
_Winnie C++ Colorizer



В первый раз я этот трюк использовал, когда делал свой парсер а-ля boost::spirit. Также он используется при реализации техники Expression Templates.

Tuesday, January 06, 2009

GCC can't parse a non-parenthesized comma in a template-id within a default argument

Ковырялся вчера с нашим Ядром (тм) и наткнулся на баг в gcc (в Comeau Online компилится), гыгыгы :)
Вот кусок кода, на котором можно протестить:


template <typename T, bool F>
struct sp
{
sp ()
{
}
};

struct X
{
typedef sp <X, true> sp_x;
//X (sp_x x = sp_x ()) // OK
//X (sp <X, true> x = (sp <X, true> ())) // OK
X (sp <X, true> x = sp <X, true> ()) // ERROR
{
}
};


Баг в багзилле тоже нашел (#57) =)

Friday, August 29, 2008

Юра Бронников, известный своим переводом SICP на русский, взялся за перевод отличной книги Types and Programming Languages. И он призывает учавствовать в вычитке. Книга планируется к изданию в бумажном виде.


via Alex Ott

Thursday, July 24, 2008

boost::python hint

При выгрузке в питон классов с виртуальными функциями wrapper, который будет перенапралять вызовы должен хранить ссылку на питоновский объект. Но в бустовой документации нигде не говорится, что у нее надо увеличивать счетчик. А делать, это желательно. Для этого нужно использовать Py_INCREF/Py_DECREF.

Saturday, July 05, 2008

Эксперименты с expression templates и sse

Недавно узнал о новой для себя технике – expression templates, это то знание, которого мне в свое время не хватило, когда я писал парсер а-ля boost::spirit (нашел, кстати, статью, в которой довольно подробно описано как устроен не весть, но часть boost::spirita).

Недавно встала задача выполнения простейших арифметических операций над большими массивами данных. Конечно, это дело можно запрограммировать по старинке, но хочется, чтобы все было красиво и прозрачно – и тут я решил поискать, как, же все таки работает boost::spirit, оказалось, что в его основе лежит очень красивая и элегантная идея (и все менее слоупоки чем я с ней давно знакомы, я думаю). Идея в том, что операции над операндами выполняются не в момент, когда эти операции встречаются в выражении, а только в тот момент, когда результат данного выражения нужен.

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

Я уже говорил, что я решил использовать эту технику для операций над массивами, и тут есть два варианта:
- использовать готовую библиотеку (например, tvmet).
- или написать свою примитивную.

Во втором случае, либо вектора должны соответствовать нужному интерфейсу, либо надо сделать небольшой адаптер (в этом случае придется написать большую кучу операторов, это видно у меня в примере). Как видно я выбрал второй вариант.

Набор классов для базовых операций (+, -, *, /) пишется за пол часа, ничего сложного нет.

Набор классов позволит нам довольно наглядно описывать действия над массивами. Кроме того, эта техника позволит избавиться от временных переменных для хранения результатов конкретных операций. В случае, когда массивы имеют большую размерность это приятный бенефит.

Второе, что я хотел сделать – это реализовать эти операции с использованием sse – во-первых хотелось наконец руками потрогать sse, во-вторых хотелось хоть не много сгладить потери в реализации, которые возможно (?) будут привнесены техникой.
Для эффективной реализации алгоритмов с sse необходимо, чтобы данные были выровнены на границу 16-байтного слова. По умолчанию стандартный аллокатор не выравнивает память и необходимо либо написать свой, либо найти аллокатор, совместимый со стандартным, который бы выравнивал выделяемую память. Как оказалось, простой аллокатор с такой функциональностью сделать совсем не сложно. Что я, конечно, и сделал.

Я сравнил что у меня получилось. Версия SSE2 обгоняет простую реализацию раза в полтора, а захардкоженная реализация тестового выражения обгоняет простую реализацию раза в два с половиной. Цифры:


Вот вроде и все. Source code.

asio 1.1.1

Между тем два дня назад вышла новая версия asio, в которой официально добавлен windows::basic_random_access_handle.

На мой вопрос связанный с тем, почему не работает async_read Chris ответил что следует пользоваться async_read_at и соответственно windows::basic_random_access_handle (пример, если кому-нибудь интересно).

Monday, June 30, 2008

Еще раз про asio и про мои тесты

У меня наконец то нашлось достаточно времени чтобы закончить начатые тесты с asio. Буду краток - asio это круто и очень удобно. Как всегда:

seq - seq - файл открыт для последовательного чтения и чтение производится последовательно
seq - rnd - тоже самое, только чтение производится со случаным смещением
rnd - seq - файл открыт для чтения с произвольным доступом, чтение производится последовательно
rnd - rnd - это понятно
seq_nb - rnd - файл открыт для последовательного чтения, без буфферизации, чтение производится со случайным смещением
rnd_nb - rnd - файл открыт для чтения с произвольным доступом, без буфферизации, чтение производится со случайным смещением

Как видно я все таки облажался в прошлом посте на тему, и чтение с произвольным смещением медленее, чем последовательное. Но при этом можно получить скорость доступа большую 10MB/s.

Реализация asio круче чем моя собственная:

Тут сравнивались только seq_nb/rnd_nb - rnd.

По нижней оси как всегда размер буфера чтения.

Thursday, June 26, 2008

asio::io_service::reset hint

Могу посоветовать перед каждым вызовом asio::io_service::run вызывать asio::io_service::reset:
This function must be called prior to any second or later set of invocations of the run(), run_one(), poll() or poll_one() functions when a previous invocation of these functions returned due to the io_service being stopped or running out of work. This function allows the io_service to reset any internal state, such as a "stopped" flag.


Это во многом облегчит жизнь начинающего asio-иста.

Tuesday, June 24, 2008

asio::windows::basic_stream_handle - 2

Если поковыряться, то можно придумать как запилить asio::windows::basic_stream_handle, чтобы он правильно (опять же на мой взгляд) читал из файлов. Запил на самом деле очень простой и я не вижу причин, почему разработчики не пошли по тому же пути.

Запил - в файл win_iocp_handle_service.hpp после строки 691 добавить следующее:
ptr.get()->Offset = boost::uint64_t (handler.total_transferred_) & 0xFFFFFFFF;
ptr.get()->OffsetHigh = (boost::uint64_t (handler.total_transferred_) >> 32) & 0xFFFFFFFF;
_Winnie C++ Colorizer


diff'ы делать я еще не научился, сорри.

asio::async_read and asio::windows::basic_stream_handle

Ковыряюсь тут с asio на предмет ассинхронного чтения файлов. Как выяснилось последняя версия - 1.1.0 - не умеет правильно читать из файлов. Простой код (см. ниже) не работает.
asio::windows::basic_stream_handle<> handle_;
asio::streambuf buf_;

asio::async_read (stream_, buf_, complete_condition_, handler_);
_Winnie C++ Colorizer


Ожидаемое (на мой взгляд) поведение - файл будет полностью считан в буфер buf_. Вместо этого буфер забивается блоком данных (по умолчанию 512b), считанных от начала файла и до тех пор пока размер буфера меньше размера файла.

По совету Alex'a взял версию c cvs.sourceforge - там та же фигня с basic_stream_handle. Но зато появился basic_random_access_handle, на основе которого можно сделать свой поток и читать последовательно (или со случаным доступом) из файла. Поток можно сделать например таким:
struct random_handle_wrapper
{
random_handle_wrapper (asio::io_service &io_service, HANDLE file)
: handle_ (io_service, file)
, file_ (file)
, transfered_ (0)
{

}

template <typename MutableBufferSequence, typename ReadHandler>
void async_read_some(const MutableBufferSequence& buffers, ReadHandler handler)
{
handle_.async_read_some_at (transfered_, buffers, handler);
}

void move_fwd (size_t transfered)
{
transfered_ += transfered;
}

asio::windows::basic_random_access_handle<> handle_;
HANDLE file_;
boost::uint64_t transfered_;
};
_Winnie C++ Colorizer


Тогда read_handler примет примерно такой вид:
void read_handler (asio::error_code ec, size_t transferred)
{
std::cout << std::string (data_.c_array(), data_.c_array() + transferred);
stream_.move_fwd (transferred);
asio::async_read (stream_, buf_, complete_condition_, handler_);
}
_Winnie C++ Colorizer


И так уже можно жить.

Saturday, June 21, 2008

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

Tuesday, June 17, 2008

Вести из зазеркалья

Данные результаты тестов официально признаются зафейлеными.

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





async (no buf, seq scan) - последовательное чтение - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_NO_BUFFERING.

async (no buf, rand) - случайное чтение - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING.

async (no buf, seq scan, rand) - случайное чтение - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_NO_BUFFERING.