Showing posts with label программирование. Show all posts
Showing posts with label программирование. Show all posts

Sunday, December 16, 2012

Отчёт о Dee Mon Winter Contest


По традиции участвовал в этом контесте в составе команды THIRTEEN. Отчёт(осторожно, там СПОЙЛЕРЫ-СПОЙЛЕРЫ-СПОЙЛЕРЫ), написанный san'ом и brox'ом с моими небольшими дополнениями можно прочитать здесь. Контест выдался замечательный, вполне в духе, автору браво и аплодисменты.

Thursday, October 11, 2012

Talk: What every programmer should know about merging branches in Mercurial

Using named branches in Mercurial can be rather difficult especially when it gets to merging these branches. There are several useful techniques and process improvements that simplify the development/release loop. You can find some of them in the talk slides I did on Oct 11, 2012 here at Quickoffice(aka DoctorMobile). It doesn't restrain any development flow and can be useful for any Mercurial (or even non-Mercurial) user. For those who are not familiar with Mercurial at all I recommend to read some guides before looking at the slides. What every programmer should know about merging branches in Mercurial Sources for the slides can be found here

Sunday, May 27, 2012

Counting bits

How would you count the number of bits set in a 32-bit integer? This question is amongst the favorite interviewing questions. The naïve approach is to loop through the bits aggregating the sum in a variable. It's already fast enough but you can make the interviewer happy with a much better "lookup table" solution: just use a static 256-entry table that contains pre-evaluated bits count for any 8-bit integer. With such table you need only 4 lookups and 4 summations to get the answer. "Lookup table" algorithm is even implemented as a GCC builtin function. Take a look at the code:

#include <stdint.h>

const uint32_t tbl[] = {
4181239173/*,lots of random data, I used 16384 integers for tests*/,2866015773 } ;

#define TEST_COUNT 100000
static const uint32_t ARRAY_SIZE = sizeof(tbl)/sizeof(tbl[0]);

int main()
{
 uint32_t i = 0;
 volatile register uint32_t c;

 for (; i< TEST_COUNT; ++i)
 {
  uint32_t j = 0;
  for (; j < ARRAY_SIZE; ++j)
  {
   c = __builtin_popcount(tbl[j]);
  }
 }
}

Here __builtin_popcount is a function that calculates the population count. If compiled with
gcc -O3 t.c -o bitcount
one can get such results:
$ time ./bitcount 
real 0m17.465s
user 0m17.425s
sys 0m0.000s
Not too fast, huh? Some of the time is wasted calling the function and inlining it can speed up things. But! There is even a better way. Compiling it with
gcc -O3 -mpopcnt t.c -o bitcount
results in:
$ time ./bitcount 
real 0m1.738s
user 0m1.728s
sys 0m0.004s
That's simply because modern processors have POPCNT instruction that can be used to count the number of bits and GCC is nice enough to support it. As you can see, in this case hardware optimization beats smart algorithm design with ease.

Resume

Algorithms are designed on paper but executed on a real hardware. Knowing what your hardware is capable of is a must for a software engineer. And please don't tell me about all these patterns and object-oriented stuff: unless you invent a machine that can run concepts instead of machine instructions, you must be aware of all the low-level stuff because it will save lives because I don't want to buy a new laptop capable of running new version of nano/Notepad.

I also highly recommend an awesome article "Benchmarking CRC32 and PopCnt instructions" for further reading.

Thursday, March 29, 2012

Ruby-like blocks and yield "keyword" in C

Today I want to show you some of the plain old C black magic. This is the magic of direct user context manipulating. You can use this low-level feature to implement some high-level language abstractions such as generators and ruby-like blocks. Let's start with an example(test1.c):

#include <stdio.h>
#include <string.h>
#include "gens.h"
#include "cblocks.h"


int main(int argc, char** argv)
{
EXECUTE_BLOCK(enumerate, (1, 10), int, x)
printf("Got %d\n", *x);
END_BLOCK
char* buf = "Hello, world!";
EXECUTE_BLOCK(enumerate, (1, 4), int, z)
printf("Saying it %d time\n", *z);
EXECUTE_BLOCK(for_each, (buf, buf + strlen(buf)), char, c)
printf("%c.", *c);
END_BLOCK
printf("\n");
END_BLOCK

return 0;
}

The code above is a valid C code. It produces such output:
Yielding 1
Got 1
Yielding 2
Got 2
Yielding 3
Got 3
Yielding 4
Got 4
Yielding 5
Got 5
Yielding 6
Got 6
Yielding 7
Got 7
Yielding 8
Got 8
Yielding 9
Got 9
Yielding 1
Saying it 1 time
H.H.e.e.l.l.l.l.o.o.,.,. . .w.w.o.o.r.r.l.l.d.d.!.!.
Yielding 2
Saying it 2 time
H.H.e.e.l.l.l.l.o.o.,.,. . .w.w.o.o.r.r.l.l.d.d.!.!.
Yielding 3
Saying it 3 time
H.H.e.e.l.l.l.l.o.o.,.,. . .w.w.o.o.r.r.l.l.d.d.!.!.

Let's take a look on the implementation of used generators (enumerate, for_each in gens.c):
#include "gens.h"
#include <stdio.h>

START_GENERATOR(int, enumerate, (int start, int end))
int i = start;
for(; i<end; ++i)
{
printf("Yielding %d\n", i);
YIELD(i);
}
END_GENERATOR

START_GENERATOR(char, for_each, (char* start, char* end))
while (start != end)
{
YIELD(*start);
YIELD(*start); // intentionally
++start;
}
END_GENERATOR

As you can see, the implementation of generators is pretty straightforward. All the magic is done in cblocks.h:
#include <stdlib.h>
#include <ucontext.h>

#define DECLARE_GENERATOR(type, name, params) extern ucontext_t name ## _context__; \
extern ucontext_t name ## _out_context__; \
extern int name ## _finished__; \
extern type name ## _out_var__; \
extern void name ## _fun__ params

#define START_GENERATOR(type, name, params) ucontext_t name ## _context__; \
ucontext_t name ## _out_context__; \
int name ## _finished__ = 0; \
type name ## _out_var__; \
void name ## _fun__ params \
{ \
name ## _finished__ = 0; \
int* current_finished = & name ## _finished__; \
ucontext_t* current_context = & name ## _context__; \
ucontext_t* current_out_context = & name ## _out_context__; \
type* current_var = & name ## _out_var__;

#define END_GENERATOR *current_finished = 1; \
}

#define YIELD(x) *current_var = x; swapcontext(current_context, current_out_context);
#define EXECUTE_BLOCK(gen, params, type, varname) do{ type* varname = & gen ## _out_var__; \
ucontext_t* current_context = & gen ## _context__; \
ucontext_t* current_out_context = & gen ## _out_context__; \
volatile int gen ## _temp_var__ = 0; \
getcontext(& gen ## _out_context__); \
if (gen ## _temp_var__ == 0) \
{ \
gen ## _temp_var__ = 1; \
gen ## _fun__ params; \
}\
else \
{ \
while (! gen ## _finished__) {

#define END_BLOCK swapcontext(current_out_context, current_context); } \
} \
} while(0);

Manual pages for swapcontext and getcontext can help in understanding what's going on here.

Resume

  • It's possible to implement nifty language features in C using its low-level features. The generators described above can be even used to build cooperative multitasking systems.
  • It doesn't mean that you should do it just because you can ;). Beware that context manipulating doesn't work on all platforms.
  • You can find the source code on github. The code should be considered just a playground so please forgive me its untidiness. If you want something mature(e.g., you're seeking for coroutines support in C), check out the libtask, libtorque or libcoro

As always I will be happy to accept any criticism on the code above. Feel free to contact me in any way you can imagine.

Wednesday, August 3, 2011

Kharkov DOU Hackathon - отчёт об участии

A hackathon, a hacker neologism, is an event when programmers meet to do collaborative computer programming(c) wiki
Вышеобозначеный хакатон проходил в Харькове 30-31 августа в соревновательном виде. За сутки нужно было написать что-нибудь интересное и команда Von Neumann Architecture в составе двух человек приняла вызов. В качестве задачи мы выбрали портирование Pascal игрушки 1994-го года на HTML5/JS посредством написания Haskell транслятора. Исходники здесь. Полноценный отчёт можно почитать здесь. Также можно посмотреть онлайн демку, демка доделывалась уже после Хакатона. Цитата:
Повторяю, вы видите как работает интерпретатор многопоточного (yield) Javascript, сконвертированного (via Haskell) из Turbo Pascal, сам интерпретатор Javascript-а "Rhino" написан на Java, портирован (via gwt + ножницы) в Javascript.
Внизу работающая программа, проверено под Chrome, Mozilla. Если в опере загрузить ее отладчик DragonFly, то там отчего-то тоже начинает работать.

Wednesday, June 22, 2011

ICFPC 2011

(((S (K (S ((S (K S)) ((S (K (S I))) K))))) ((S (K K)) K)) awesome! was ICFPC)

ICFP Contest 2011 точно войдет в список лучших ICFP контестов всех времен. Причина тому - интересное непутанное задание, которое при этом довольно сложное и хорошо продуманное. Я, как и в прошлом году, принимал участие в составе команды THIRTEEN. Отчёты от команды можно найти на сайте http://thirteen.kharkov.ru(сейчас там только один отчёт). Здесь постараюсь дать описание контеста со своей точки зрения.
Для меня контест начинался вдали от команды(я находился в командировке в Лондоне). Прочтение задания вызвало бурную радость(я как раз готовился к контесту написанием парсера и эвалуатора для лямбда-калькулюса). Из-за удаленности от команды я решил код не писать, а сосредоточиться на изучении того, что мы можем сделать с помощью карт. Это оказалось правильным решением - мои коллеги написали всё намного быстрее и лучше, чем с моим участием :) Первая ночь прошла в попытках использовать Y комбинатор для создания рекурсивных функций. К сожалению, strict evaluation не давал этого сделать. И даже Y комбинаторы для applicative order не работали. Также не получалось и организовать рекурсию через SII. Утром пятницы я отправился на работу, но потратил большую часть рабочего времени на исследование системы. Результатом этого стала "недорекурсия" через get ячейки. А по окончанию рабочего дня пришло лучшее понимание того, как работает вычисление в предложенной системе, и это дало возможность вывести настоящую рекурсию по аналогии с выводом Y комбинатора. Это позволило писать рекурсивные функции, пробегающие по всем ячейкам. К счастью, организаторы ограничили количество аппликаций одной тысячей(к счастью - потому что увеличение количества аппликаций до 3839 позволило бы уничтожать противника где-то на 85 ходу). И из-за этого ограничения рекурсивные функции пробегались только по нескольким ячейкам и самоуничтожались после применения.
К этому моменту в Харькове был написан конвертер из лямбда выражений в SKI выражения и конвертер из SKI выражений в программы. В ночь с пятницы на субботу я пытался оптимизировать конвертер и генерировать с его помощью выражения, вызывающие рекурсивный обход ячеек. Результат: больше 89 ячеек за один запуск для односложных команд при помощи настоящей рекурсии не обойти. Ночью же вылетел в Харьков(и наконец выспался в самолете). По прилету обнаружил, что коллеги вовсю думают над однотактными и двухтактными пушками. Идея была в том, чтобы создать пушку, которая будет уничтожать вражескую ячейку и восстанавливать после этого vitality. Двухтактная пушка перенаправлялась на следующую ячейку двумя тактами, однотактная(гениальное изобретение Sas/Rst) - одним. К вечеру пришло осознание того, что нам нужно писать хитрого стратега, который будет решать, какие выражения применять. San создал инфраструктуру для написания ботов-стратегов, и к этому моменту был написан язык для описания действий(by San/Dzhulgakov). Я продолжал в душе лелеять надежду на использование рекурсивных выражений и начал создавать ботов и стравливать их в турнире. Конечным результатом этого процесса к 10 утра воскресенья стал SuicideBot(убийца-камикадзе) и, главное, ZombaBot - бот, который за ~150 ходов уничтожал ~40 ячеек противника, а еще за ~90 ходов стирал всю информацию из них. Начальная стратегия использовала настоящую рекурсию(синтаксис языка не приводится):
power@0 = 8192;
@1 = (S (attack 0 0) (attack 1 0) (get 0));
pois@2 = ^x. (help x x @power);
part@3 = ^n.^f. (f (S @pois succ n) f);
part_func@4 = ^y. (@part (y 0) ) @part;
@5 = (dec 0) (zombie 0 @part_func);
newpois@6 = ^xx. (zombie xx I);
newpart@7 = ^nn.^ff. (ff (S @newpois succ nn) ff);
newpart_func@8 = (@newpart 219) @newpart;

Также в это время формируется SanBot(by San) - консервативный бот, который умеет защищаться, уничтожая "горячие" ячейки противника. В свободное время он собирает более быстрые атакующие программы и переключается на них. San постоянно развивал своего бота, тренируясь на кошках - на SuicideBot и ZombaBot. В схватках с ZombaBot он научился закрывать порталы для зомби, а по дороге научился воскрешаться, восстанавливать команды из кеша, красть константы. ZombaBot тоже не стоял на месте. Sas подхватил идею с использованием copy для организации рекурсии и придумал, как с ее помощью сделать обход ячеек. В конечном итоге, этот способ и оказался самым эффективным способом обхода. Он дал возможность ZombaBotу полностью уничтожать противника за 4 запуска рекурсивного зомби, за 198 тактов. К этому времени submissionы SanBotа показали, что он слишком консервативен, и поэтому не столь эффективен. Воспользовавшись наработками, я добавил еще и UndeadRiseBotа - бота, который пытался уничтожить противника за 198 ходов, а после этого переключался на SanBotа. Этот бот оказался весьма эффективным - он крошил противников на неофициальном сервере и даже побеждал самого SanBotа, потому что использовал его же механизмы лечения. Мы продолжали развивать эту идею и оптимизировать код. Результат(язык получился довольно мощный и выразительный, а к концу контеста Dzhulgakov добавил в него и средства для ручной оптимизации):
power@0 = 8192;
@24 = (attack 0 0) (get 0);
@24 = (attack 1 0) (get 0);
pois@1 = ^x. (help x x @power);
!clear @0;
part@0 = \x. ((@pois x) (copy (K 0 x)) (succ x));
!clear @1;
part_func@1 = ^z.^y. ((get 0) (y z));
@128 = (S zombie @part_func);
// optional dec 0; here is done by UndeadRiseBot
@128 += @@ 0;
address@2 = 66;
@129 ~ zombie1 = (zombie 0 (@part_func @address));
// optional dec 0; here is done by UndeadRiseBot
!continue zombie1;
@2 += dbl @@;
@130 ~ zombie2 = (zombie 0 (@part_func @address));
// optional dec 0; here is done by UndeadRiseBot
!continue zombie2;
!clear @2;
@2 = 198;
@131 ~ zombie3 = (zombie 0 (@part_func @address));
// optional dec 0; here is done by UndeadRiseBot
!continue zombie3;

Этот вариант уничтожал противника за 177 ходов. В копилке наших знаний есть еще парочка оптимизаций, которые должны уменьшить код до 171-172 ходов. Хочется верить, что это минимальная программа, которая уничтожает безвольного противника.
Всё это время Sas собирал своего бота - SasBot, который использовал супероружие - зомбопушку (убить+стереть). Он добился того, что своей пушкой уничтожал и SanBotа, и UndeadRiseBotа. Но, к сожалению, он был готов слишком поздно и не очень хорошо справлялся с реальными противниками на официальном сервере, поэтому в последний момент было решено сабмитить UndeadRiseBota, который в душе - SanBot.

Выводы по окончанию контеста: по результатам битв на неофициальном сервере видно, что наш бот неплох, но далеко не лучший. Что особенно интересно - есть сильные боты, которые используют сходную с нами тактику, но при этом побеждают нас. Это значит, что мы просто не отладили бота окончательно. Еще стоит пожалеть о судьбе SasBotа. Он был готов слишком поздно, чтобы его достижения использовать в SanBot/UndeadRiseBot, и не был готов к submission самостоятельно. Основной вывод - нужно было начинать создавать ботов и описывать стратегии раньше. Тогда к концу контеста у нас бы было лучшее понимание того, какая тактика оптимальна.

Как я уже говорил выше - один из лучших ICFP contests за всё время, что я о нём знаю. Спасибо организаторам - великолепное задание и отличная организация. Спасибо каждому из команды THIRTEEN. Мне кажется, мы отлично сыгрались и можем и будем таким составом выигрывать :)

Friday, April 1, 2011

Talk: (Non) Comprehensive Guide to C/C++ Source Code Quality

I did a talk on C/C++ source code quality tools here at Quickoffice. I believe it could be helpful for the C/C++/ObjC developers(especially for those who care about quality). Here goes the list of the tools mentioned:

  • CPD
  • gcc
  • clang
  • Valgrind
  • cppcheck
  • EDoc++
  • GCOV
  • gcovr
  • LCOV
  • avalanche
  • frama-c
(Non) Comprehensive Guide to C/C++ Source Code Quality
You can find LaTeX source and .cpp source files at github

Wednesday, June 23, 2010

Отчёт об ICFPC-2010

До чего дошёл прогресс -
До невиданных чудес!
Опустился на глубины
И поднялся до небес.

Позабыты хлопоты, остановлен бег -
Вкалывают роботы, а не человек.

Введение

ICFPC-2010 закончился еще в понедельник, но он оказался настолько емким, что полтора дня я только и мог, что кататься на велосипеде и проветривать мозги. Да и сейчас на подробный отчёт меня не хватит, так что ограничусь только кратким описанием того, что происходило со мной во время ICFPC-2010.
Итак, начнём с того, что в этом году меня пригласили поучаствовать вживую в харьковской команде THIRTEEN. Команда сыгранная и довольно сильная, до этого я с ними сталкивался на Sapka и ICFPC-2009.
На время прохождения контеста мы все собрались в квартире-офисе одного из членов команды. Отсюда первое и самое сильное впечатление: общение с живыми людьми, участие действительно в реальном времени(без перерывов на сон в собственной постели) - это нечто невообразимое. Представьте, что вы провалились в кроличью нору и попали в страну, где жители решают странные задачки, где сон и еда - досадные раздражители, а производительность труда взлетает до небес.
Второе впечатление - для участия в таком конкурсе нужна не команда из программистов, каждый из которых будет писать код. Нужны математики, физики, спецы по схемотехнике и прочие головастики. А программить могут 2-3 человека, не более того.

Кратко о происходившем

Задание повторять не буду, вы можете найти его краткое описание в отчете Futamura Rejection. В первую очередь, все бросились разбирать формат фабрики. Довольно быстро я написал парсер на Python, который генерил картинки в graphviz. Однако красивыми картинки сделать не получилось и в итоге все фабрики рисовались на бумаге. Как только с форматом более-менее разобрались, начали подбирать функцию гейтов. Мудрым решением здесь было предположить, что Undefined сигнала не существует и он заменяется 0. Я сделал простейший эмулятор на Python и с его помощью пытался подобрать функцию гейта(в виде матрицы). Но где-то в коде закрался баг и пока я его искал, sannysanoff на Java реализовал такой же симулятор без багов и нашел эту самую функцию.

А дальше пошло самое интересное: нужно было придумать генератор, который будет генерить фабрики в соответствии с нужной выходной последовательностью. При этом на входную последовательность завязываться было нельзя. Пока я крутил в голове всякие мысли со стейтами, переходами между ними и применением алгоритмов из теории графов для построения таблицы переходов между стейтами, Rst7 и SAS мозговым штурмом предложили довольно простой и естественный способ генерации фабрик из таких макроэлементов, как "генератор 0", "генератор 1", "генератор 2", "задержка", "задержка с вычитанием", "вычитатель". sannysanoff его быстро закодил(кстати, я впервые видел применение настоящего ООП в спортивном программировании. До этого я считал, что ООП слишком затратно, чтобы его применять при жестких временных рамках). Фабрики получались довольно большими, но работали.

Это был вечер пятницы. Начиная с этого момента начинается хмурая и безрадостная эпопея разгадывания внутреннего кода машин и топлива. Одна из причин этой хмурости (и один из основных лучей ненависти к организаторам) - в том, что попытки работать из под одного IP на нескольких машинах заканчивались провалом. Видимо, организаторы сделали привязку по IP и, соответственно, если заходить всем под разными аккаунтами, то аккаунты шарились между машинами, а при заходе по одному аккаунту высвечивался Internal Error. В итоге, я и sannysanoff зарегистрировали два фейковых аккаунта (fake001 и fake002) и использовали их через собственные туннели. Аккаунт THIRTEEN был под управлением brox и через его машину мы сабмитили официальные решения. Таким образом, несмотря на то, что участников было довольно много, разбираться с форматом данных могли только два, максимум три человека: ведь для того, чтобы понять формат, нужен был доступ к серверу.

Суббота прошла туманно. С утра я написал на Python скрипты, которые позволяли заливать тривиальные решения для тривиальных машин. Это дало ощутимый прирост к очкам. Всё остальное время было посвящено разгадыванию кода. Почему-то это шло очень медленно и к вечеру субботы полного понимания даже структуры топлива еще не было. Более-менее был ясен только способ кодирования чисел. Где-то в это же время я написал command-line интерфейс к парсеру на сервере, открыл к своей машине терминальный доступ и за разгадывание формата взялся Rst7. После нескольких часов, он предложил вариант формата, который мы успешно(!) использовали до утра понедельника. После этого Rst7 и SAS увлеклись идеей оптимального генератора фабрик. Они его разработали, хотя я до сих пор лучше понимаю, как работает первый вариант генератора.

Таким образом, к началу воскресенья у нас не было еще формата описания машин и не было никаких наработок по алгоритмам их решения. Я не помню точного времени, но где-то в этот момент, руководствуясь подсказками из чатов, до меня начал доходить формат описания машин. Однако полностью это понимание сформировалось позже. Читая отчёты других команд, я завидовал тем людям, которые выписав колонки чисел, сразу догадались, что два формата чисел отличаются сдвигом и отбрасыванием двойки. Я к этому сдвигу шел долгие часы, а 22 вообще долгое время считал признаком начала списка. Всё же, совместно с sannysanoff мы разобрали этот формат, я написал простенький енкодер машин. Еще ранее был написан интеллектуальный посылатель тривиальных машин. Он проработал совсем немного - до момента падения сервера. После поднятия сервера организаторы отключили нужные для работы ссылки на список всех машин, но оставили рабочей скрытую в недрах ссылку на список всех машин с принадлежностью к командам. Переписать скрипт, чтобы он использовал эту ссылку - дело 3-х минут. Как я понимаю, к моменту, когда мой бот снова начал работать, остальные участники еще не успели модифицировать собственные скрипты - скорость работы сервера была великолепна!

Также где-то в ночи появился переборный решатель от sannysanoff и dshoot. К нему так же был написан скрипт на Python, который запускал программу на Java для нахождения решений и отсылал результаты на сервер. Так решались небольшие нетривиальные машины. В это же время стало очевидно, что мы многое теряем, н придумывая свои машины. Этим занимались SAS и brox. В это же время действовало ограничение на 100 символов на машину. Поэтому почти все наши машинки - небольшие и поддаются перебором. К сожалению, мы пропустили момент, когда ограничение на 100 символов было снято.

Утром в понедельник вовсю работали скрипты, мы постили шаблонные машинки, а sannysanoff обнаружил, что топливо у нас кодируется неправильно. Часов за 7 до окончания контеста. Однако проблему удалось решить, хотя это нам и не сильно помогло. Последние участники разбрелись за час до окончания.

Итог

30 место, 69 машинок, около 700 топлив. Великолепное задание, отвратные кодировки. Я успел покодить на Python, C и Java. Кстати, этот ICFPC-2010 - наглядное доказательство того, что скриптовые языки вроде Python - очень нужны в таких контестах. Без Python не было бы ботов, написанных в считанные минуты, без Python не было бы быстрого прототипирования.

Заключение

В голове у меня эти дни отпечатались размыто. Действительно, как сон Алисы о Стране Чудес. Я мог перепутать время, потому что не помню, когда заканчивался один день и начинался другой. Я ничего не упомянул о том, как brox пытался на С подобрать очень короткие макроэлементы а я писал ему алгоритм пермутаций, как я оптимизировал генераторы 1 с задержкой, как вместе с Rst7 мы пытались решить машины аналитически с помощью Matlab, как я пытался реализовать рекурсивный энкодер чисел и как у меня ничего не вышло.

Немного сумбурный отчёт от sannysanoff. Если чей-то ник или какие-то события я указал неправильно - поправляйте.
ICFPC 2010 - кулаками после боя (by brox)

Wednesday, March 17, 2010

Некоторые трюки при использовании GDB

Небольшое собрание трюков, которые могут помочь при отладке с использованием GDB. Не претендует на сенсационность, но должно быть полезно для тех, кто только начинает знакомиться с этим замечательным инструментом. Итак, начнём:

Печать содержимого STL контейнеров

Основные способы собраны здесь, но я бы хотел остановиться на наиоболее универсальном способе - gdb-stl-views(нужно добавить в ~/.gdbinit). Он будет работать не только в самых новых версиях, а и в уже устаревших (это является важным фактором для тех, кто не может обновиться). Примеры использования:
(gdb) plist some_list
List size = list_size
List type = std::list<element_type, std::allocator<element_type> >
Соответственно, для того, чтобы распечатать весь список нужно использовать:
(gdb) plist some_list element_type
elem[0]: $i = ...
...
elem[list_size - 1]: $i = ...
List size = list_size
А теперь интересные моменты:
  • Если внутри контейнера хранится умный указатель, то часто для упрощения вывода можно в качестве element_type указать просто указатель (зависит от того, какой умный указатель вы используете. Большая часть из них при reinterpret_cast к указателю даст осмысленный результат). Например, вместо
    plist some_list smart_ptr<elem_type>
    использовать
    plist some_list elem_type*
  • Если вы активно используете пространства имён, element_type скорее всего будет доступен только с полным указанием пространств имён, например NS1::NS2::element_type. Интересной особенностью GDB является то, что он не воспринимает NS1::NS2::element_type* как тип. Обойти эту проблему можно заключив имя типа в одинарные кавычки: 'NS1::NS2::element_type'*
  • GDB иногда игнорирует using namespace. Поэтому лучше просто полностью указывать имя типа со всеми пространствами имён
  • Чем новее у вас версия GDB, тем лучше он работает с пространствами имён. Если есть возможность - поставьте GDB 7.0 и выше

Вывод строк

Если вы активно работаете с разными типами строк, с разным размером символов, то вам пригодится следующий совет. Чтобы вывести строку, которую не желает выводить print, можно использовать:
  • Команду просмотра памяти x. Например,
    (gdb) x/16s some_char_ptr
    для вывода 16 первых символов в символьном виде
  • Можно добавить вот такую функцию в .gdbinit:
    define wchar_print
    echo "

    set $i = 0
    while (1 == 1)
    set $c = (char)(($arg0)[$i++])
    if ($c == '\0')
    loop_break
    end
    printf "%c", $c
    end

    echo "

    end
    Использовать так:
    (gdb) wchar_print some_char_ptr
    Однообразно выводит std::string, std::wstring, различные двухбайтные строки. Естественно, конвертация к ASCII строке приводит к потере информации, однако в большинстве случаев это не так важно

Step Out

Самый короткий совет. Step Out делается командой fin или finish. Это почему-то один из самых распространённых вопросов.


И the last but not the least: в последнее время я довольно редко пишу в этот блог. Одна из причин тому - для небольших спонтанных записей я веду микроблог. Вторая - банальная нехватка времени. Однако не спешите отписываться: в записной книжке скопилось множество интересных статей, советов и идей. Постараюсь возобновить более-менее равномерное наполнение этого блога.

P.S. Для вывода на экран Qt строк можно использовать вот такое:
define printqstring
printf "(QString)0x%x (length=%i): \"",&$arg0,$arg0.d->size
set $i=0
while $i < $arg0.d->size
set $c=$arg0.d->data[$i++]
if $c < 32 || $c > 127
printf "\\u0x%04x", $c
else
printf "%c", (char)$c
end
end
printf "\"\n"
end

Monday, February 1, 2010

Краткий отчёт о PyCamp

Не далее как в прошлую субботу, 30 января 2010, в Киеве состоялась конференция PyCamp Kyiv, основной темой которой был язык Python и сопутствующие технологии. Мне повезло там побывать, и я получил огромное удовольствие от конференции.

Доклады

В основном, доклады были довольно интересными. Ниже представлены некоторые претензии/отзывы по докладам, а также оценка качества повествования/слайдов:
  • Александр Шигин "Почему Python - тормоз и как заставить его меньше тормозить" - неплохой доклад о том, какие конструкции в Python тормозят сильнее, чем другие. Для многих информация из доклада может показаться очевидной, но для меня она оказалась довольно полезной - позволила кое-как классифицировать разрознённые знания по этой теме. Качество повествования-3+, качество слайдов-5.
  • Юрий Юревич "Рецепты декораторов" - очень короткий и довольно малоинформативный доклад о том, что такое декораторы в Python. Несмотря на хорошие слайды(на 5) и доклад(на 4+)
  • Дмитрий Кожевин "Программирование на нервах" - отличный развлекательный доклад на тему управления проектами. Он небольшой и веселый, поэтому смотреть в видеозаписи обязательно. Качество доклада - 5, качество слайдов - 3.
  • Михаил Кашкин "Работа с хранилищем данных в Google App Engine, отличия от реляционной модели" - доклад сочетает в себе общий обзор Datastore и описание некоторых особенностей. Доклад, в принципе, неплохой, но неполный. После него осталось очень много вопросов. Качество слайдов - 5, качество повествования - 4.
  • Александр Соловьев "Redis: Дикий Запад баз данных" - хороший доклад про сверх-быструю key-value db. Удачно выбранная тема в данном случае на 80% определила успех доклада. Качество повествования - 5, качество слайдов - 5
  • Андрей Светлов "Безопасная разработка ПО. Результат длинного пути и множества набитых шишек" - доклад о том, как важно использовать тесты, системы автоматической сборки и прочие вспомогательные тулзы. Довольно длинно, в основном по местам давно обглоданным. К тому же для многих тема "Тестировать или не тестировать" уже далеко не так остра, как была пару лет назад. Качество повествования - 4, качество слайдов - 4.
  • Владимир Пузанов, Владимир Кириллов "Расширение и встраивание Python" - обзорный доклад об интерпретаторах Python, методах расширения стандартной функциональности и связывание с другими ЯП. Было весьма интересно, а продемонстрированный в конце хак нахожу очень приятным и довольно юзабельным. Качество повествования - 4+, качество слайдов - 3.
  • Андрей Мишковский "Использование Python в ГИС" - неплохой обзорный доклад о средствах и библиотеках для создания ГИС на Python. Хотя я бы с большим удовольствием послушал о том, как создаются ГИС, а не с помощью чего(эту информацию можно получить и в Google). Качество повествования - 5, качество слайдов - 5.
  • Сергей Кириллов "WebSockets в Twisted" - неплохой вводный доклад на тему "Что такое WebSockets?". Тема не очень сложная и была полностью раскрыта. Качество повествования - 5, качество слайдов - 5.
  • Александр Бельченко "Интернационализация и локализация Python-приложений с использованием gettext" - подробный доклад об использовании gettext для локализации GUI приложений на Python. Большую часть информации из доклада можно получить при чтении документации, но докладчик также обозначил множество интересных и полезных моментов. Качество повествования - 4+, качество слайдов - 5.
  • Иван Моргун "Работа с платежными системами в Django (PayPal, WebMoney)" - излишне подробный доклад, практически пересказ документации. Доклад можно было существенно порезать и он от этого стал бы более привлекательным. Качество повествования - 3+, качество слайдов - 3+.
  • Дмитрий Жемеров "PyCharm – новая python IDE от JetBrains" - весьма любопытная презентация новой коммерческой IDE для Python. Выглядит весьма многообещающе, но то, что оно написано на Java, а также будет в будущем стоить $100, слегка портит впечатление. Качество повествования - 5, качество слайдов :) - 5

Организация

Организована конференция была просто восхитительно: с онлайн-трансляцией, вкусными бутербродами, с вайфаем, с прямой трансляцией на большой экран сообщений из твиттера (мои сообщения). И всё это практически бесплатно. Выражаю благодарность организаторам - Максу Ищенко и Владимиру Гоцику.

Заключение

Жду видео. Побольше бы таких конференций, хороших и разных!

Sunday, September 27, 2009

Мини-отчёт об участии в GCJ

GCJ(Google Code Jam) - контест по спортивному программированию, который проводит всем известная компания Google. Контест не командный, международный, проводится в онлайн.
В этому году участвовал в первый раз, основные результаты таковы: остановился на втором раунде, в третий не прошёл.

Впечатления

Сумбурные. Участие в ICFPC и в Sapka рождает совсем другие эмоции. GCJ более напряженный, задачи значительно меньше по объему, но труднее, временные рамки куда жестче. Однако задачи GCJ отличаются и от тех, что встречаются на ACMовских олимпиадах. Большинство задач действительно имеют очень короткие и очень красивые решения, каждую из них действительно можно решить за отведенное время, если уловить какую-то важную мысль.

Выводы

  • Неожиданно для себя обнаружил доказательство тезиса "Разные ЯП - для разных задач". Решал задачи на Python(основной язык), Haskell и С++ - действительно, иногда тот или иной язык куда лучше подходил для решения определенной задачи
  • Фан можно получать и от простых задачек, не обязательно рулить спутниками или писать AI для bombermanа. Зря я почти забросил теорию - теперь сыплюсь на стандартных алгоритмах и мне очень стыдно
  • Нужно обязательно участвовать в следующих GCJ. Он совсем не напрягает и занимает куда меньше времени, чем многодневные командные состязания

Интересная ссылкa

Недавно мне подбросили ссылку на еще один контест - Hugi Size Coding Competition Series(hugi-compo), который кардинально отличается от всех, в которых я доселе участвовал. Именно этим он и привлекателен, хоть я и не обладаю достаточной квалификацией для удачных выступлений(контест посвящен ассемблерной оптимизации). Возможно, кого-нибудь заинтересует такое нестандартное соревнование. Спасибо за внимание!

Saturday, July 11, 2009

Сказ о том, как Python С++ обогнал

Я всегда защищаю С++, когда приверженцы других языков ругают его за невыразительный синтаксис, чрезмерную сложность или раздутость, потому что, по-моему мнению, на С++ можно писать красиво, и особенно в этом помогают различные высокоуровневые конструкции из стандартной библиотеки.
В одном из споров в c_plus_plus@c.j.r речь зашла о том, какой же из языков лучше - простой С или всё же С++? В качестве одного из аргументов я предложил провести эксперимент - сколько времени понадобится для решения простой задачи: из stdin считываются строки текста, и после этого в обратном порядке записываются в stdout, т.е., первая строка в stdin становится последней в stdout. Длина строк не ограничена, поэтому для решения ее на С понадобится выделять и перевыделять память, от этого код на С разбухнет, что станет наглядной демонстрацией превосходства высокоуровневых конструкций С++. Более того, я предположил, что код на С++ может быть даже производительней кода на С, написанного без дополнительного профилирования - ведь контейнеры С++ выделяют память так, чтобы reallocate происходил реже. Пример кода на С++, написан за пару минут для демонстрации:

#include <iostream>
#include <string>
#include <vector>

int main() {
std::vector<std::string> v;
while(std::cin)
{
std::string s;
std::getline(std::cin,s);
v.push_back(s);
}
for (std::vector<std::string>::reverse_iterator it = v.rbegin();it!=v.rend();++it)
{
std::cout<<*it<<"\n";
}
}
Как видите, код далеко не оптимален, однако мне хотелось показать, что даже такое решение лучше, чем код на С.
Тут же захотелось проверить, а как другие языки справятся с этой задачей. Например,однострочник на Ruby:
$stdin.readlines.reverse.join.display
Когда я протестировал его на небольших файлах, С++ решение оказалось быстрее решения на Ruby в десятки раз, что меня нисколько не удивило. Однако для чистоты эксперимента я решил увеличить размер файла. Я сгенерировал файл в 100 строк общим размером ~600mb(исходник на Python):
open("/tmp/z.txt",'w').write((''.join(map(str,xrange(1000000)))+'\n')*100
Результаты тестирования на нём были просто поразительны:
ruby 1.8.6 (2009-06-08 patchlevel 369) [i686-linux]:
real 0m9.841s
user 0m0.849s
sys 0m7.206s

gcc (GCC) 4.1.2 (Gentoo 4.1.2 p1.3):
real 0m51.902s
user 0m48.682s
sys 0m1.429s
Я тут же решил проверить решение на Python:
import sys
sys.stdout.write('\n'.join(reversed(sys.stdin.readlines())))
Результаты:
Python 2.6.2
real 0m4.855s
user 0m0.913s
sys 0m1.233s
Т.е., еще быстрее, чем ruby (что уже не удивительно ;) ). Просмотрев код на С++, я добавил простейшую оптимизацию, которую изначально добавлять не хотел, чтобы код совсем не был похож на С. Я добавил хранение в векторе указателей, а не строк, что должно было убрать ненужное копирование:
#include <iostream>
#include <string>
#include <vector>

int main() {
std::vector<std::string*> v;
while(std::cin)
{
std::string * s = new std::string();
std::getline(std::cin,*s);
v.push_back(s);
}
for (std::vector<std::string*>::reverse_iterator it = v.rbegin();it!=v.rend();++it)
{
std::cout<<*(*it)<<"\n";
delete *it;
}
}
Но результат всё так же не вдохновляет:
real 0m46.444s
user 0m43.462s
sys 0m1.379s
Назначив ответственным за тормоза std::string, я попытался переписать всё на QString из Qt, которые, по слухам, умеют copy-on-write и вообще быстрые. Наивный код:
#include <iostream>
#include <QtCore/qstring.h>
#include <QtCore/qlist.h>
#include <QtCore/qtextstream.h>
#include <vector>

int main() {
QList<QString> v;
QTextStream st (stdin);
QTextStream sto (stdout);
while(!st.atEnd())
{
v.push_back(st.readLine());
}
QListIterator<QString> it(v);
it.toBack ();
while (it.hasPrevious ())
sto<<it.previous()<<"\n";
}
Однако это решение пожрало все 2 гигабайта моей оперативной памяти и я его остановил, попросив Qt-шников с c_plus_plus@c.j.r помочь с оптимизацией. После нескольких минут получился вот такой код, который использовал уже 1.4 гигабайта оперативки:
#include <iostream>
#include <QtCore/qstring.h>
#include <QtCore/qlist.h>
#include <QtCore/qtextstream.h>

int main ()
{
QList<QByteArray> v;
QTextStream st (stdin);
QTextStream sto (stdout);
int line = 0;
while (!st.atEnd ())
{
std::cerr << "Reading line... " << line++ << std::endl;
v.push_back (qCompress (st.readLine ().toUtf8 (), 9));
}
QListIterator<QByteArray> it (v);
it.toBack ();
while (it.hasPrevious ())
{
std::cerr << "Writing line... " << std::endl;
sto << qUncompress (it.previous ()) << "\n";
sto.flush ();
}
}
Однако он работал еще медленнее, чем на стандартных строках - более двух минут. Для чистоты эксперимента я также написал вариант на Python, который не использовал стандартные join и reversed, а был более похож на код на С++:
import sys
s = sys.stdin.readlines()
for x in xrange(len(s)-1,-1,-1):
sys.stdout.write(s[x]+'\n')
Его результаты:
real 0m2.198s
user 0m0.884s
sys 0m1.190s

Выводы

Если не учитывать опыты с Qt(всё равно ведь хороших результатов добиться не удалось), ни в одном из языков не было использовано каких-либо особых оптимизаций, напротив, были использованы те конструкции высокого уровня, которые предлагает сам язык. И удивительно, что программы на Python и Ruby смогли с большим отрывом обогнать аналогичную программу на С++. Я сам еще не знаю причину этого, и я был бы очень рад, если бы кто-то смог прогнать аналогичные тесты у себя на машине. Однако вне зависимости от причины, такой результат явственно говорит одно - нельзя безоговорочно верить в то, что С++ быстрее интерпретируемых языков, особенно если простейшие высокоуровневые конструкции в них, такие как строки, работают лучше.

P.S.

Я не старался создать идеальные условия для тестирования и не прогонял тесты сотни раз, чтобы получить идеальный результат. Достаточно всего-лишь заметить, что в этом синтетическом тесте Python был в 2-5 раз быстрее Ruby, а Ruby была в 4-5 раз быстрее С++. Спасибо за внимание!

Частичная реабилитация(update)

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

int main() {
std::ifstream fin("/dev/stdin");
std::vector<std::string*> v;
while(fin)
{
std::string * s = new std::string();
std::getline(fin,*s);
v.push_back(s);
}
for (std::vector<std::string*>::reverse_iterator it = v.rbegin();it!=v.rend();++it)
{
std::cout<<*(*it)<<"\n";
delete *it;
}
}

real 0m1.783s
user 0m0.656s
sys 0m1.070s

Т.е., тормозил std::cin. Естественно, это не оправдание, поэтому реабилитация только частичная. Вывод: tools don't kill software. people kill software.

Tuesday, June 30, 2009

ICFP contest 2009

Продолжая добрую традицию подробно описывать контесты, начатую с Sapka contest, предлагаю вашему вниманию отчёт о ICFPC 09

Введение

ICFP Contest - командный контест, который проводится один раз в году. Количество участников в команде не ограничено. Задание одно, на весь контест отводится 72 часа(3 суток). Контест делится на lightning round(оцениваются решения, полученные в первые 24 часа) и main round(оцениваются все отосланные решения).

Команда

Страницу команды Concrete mixers можно найти здесь. Т.е., 4 человека, но после lightning A2K отошел от дел. С одним из оставшихся участников - xa4a - я уже участвовал в Sapka, и мы там даже взяли призовое место на Lightning. Со вторым из оставшихся - Murkt - до этого работать вместе не приходилось, но мы вроде неплохо сработались.

Инструменты

Основной язык - Python. В качестве системы контроля версий использовали Mercurial, в качестве хостинга - bitbucket. Для визуализации был использован pygame(также были попытки использовать Qt, но в итоге остался вариант с pygame). Для общения использовали конференцию в jabber.

Задание

Оригинальное задание (последнюю версию) можно скачать здесь. Кратко - нужно было писать управляющие программы для спутника для выполнения разных задач. Поведение спутника и окружающей его вселенной эмулировалось в бинарниках, которые предоставляли организаторы. Бинарники можно было запустить на виртуальной машине, спецификации которой были также предоставлены. Список маневров, которые нужно было выполнять со спутником:
  • Перевод спутника с одной круговой орбиты на другую
  • Рандеву с другим спутником, двигающимся по круговой орбите
  • Аналогичное рандеву, но начальная орбита и конечная могут быть эллиптическими
  • Упрощенное рандеву с 11-ю спутниками на произвольных орбитах. Также здесь присутствует Луна и заправочная станция
Таким образом, большая часть задания связана с орбитальными маневрами - сплошная математика и физика.

Ночь первая (26.06-27.06)

Получили задание, прочитали, пообсуждали, устранили непонимания, распределили задания. Где-то через два часа у нас наконец появился парсер бинарников, еще через час было две VM, из которых мы выбрали лучшую. Еще через два часа я сделал первый тестовый солвер с визуализатором, а к этому моменту xa4a уже залил нужные формулы для hohmann transfer, A2K доделывал логгер, который был нужен для формирования сабмишенов.
Т.о., через 5 часов после начала я приступил к прикручиванию формул к солверу, однако это оказалось не так просто. Murkt с чувством выполненного долга(написанная им VM работала, хоть и медленно) пошел спать, а мы с xa4a и A2K еще часа два пытались починить логгер и формулы, A2K писал визуализатор на Qt. Результат первой ночи - готова VM, визуализатор, основная инфраструктура, однако очков всё еще 0

День первый (27.06)

С утра Murkt и xa4a подхимичили формулы и у нас в нашем симуляторе появились первые очки. Однако при попытке сабмита тут же стало понятно, что написанный ночью логгер ошибочен и я взялся его переделывать. В 14:20 был "EPIC WIN!!!!!"(с)Murkt - первые очки, полученные после исправления логгера. Задачи 1001-1004 в этот момент времени перешли в статус решенных. И пока xa4a и Murkt продолжали изыскания с задачами 2001-2004, я в течение часа многократно ускорил VM путем генерации и последующей интерпретации кода на Python. Также скриншот, сохранившийся с этого временного участка:
В дальнейшем мы все вместе пытались побороть задачи 2001-2004. Напомню, в этих задачах нужно было не просто перелететь на другую орбиту, как в 1001-1004, а и попасть еще и в ту же точку этой орбиты, что и другой спутник. Для того, чтобы этого добиться, мы ввели понятие hohmann delay - время ожидания, нужное, чтобы после него при hohmann transfer попасть в нужную точку. В 18:40 Murkt получил первые очки этим методом. Однако метод оказался совсем не стабильным - решение он давал, но давал и большие погрешности. Поэтому оставшееся до окончания lightning время мы работали в двух направлениях - устранение погрешности и 3001-3004. Ничего существенно добиться мы не успели и lightning закончили с результатом где-то 945 баллов. В top lightning'a мы,естественно не попали, и место в lightning на данном этапе узнать невозможно, хотя и очень интересно.
Ниже - визуализатор, который писал A2K, но который так и не был использован:

Ночь вторая (27.06-28.06)

Появилась Луна и задачи 4001-4004. Часа через четыре усилиями Murkt и xa4a были существенно проапгрейджены решения наших 8 задач и получено 1127.44746 очков. Приблизительно этого хотелось добиться в lightning, но не успели, а жаль. Также xa4a очень красиво переделал солверы (стало чем-то похоже на twisted). С 3х до 6 утра я сделал алгоритм подгазовки через phasing - он работал идеально и позволял проходить 2001-2004 даже без hohmann delay.

День второй (28.06)

Murkt почистил код, а мы с xa4a пытались найти параметры эллиптических орбит и у нас никак не получалось вывести формулу, которая бы работала для всех задач 3001-3004. В поисках решения были привлечены ЧМ и придумано несколько странных методов определения. Однако это ни к чему не привело, а уравнение орбиты было выведено xa4a чуть позже, когда я отсутствовал.

Ночь третья (28.06-29.06)

Murkt сделал naive chase - подгазовку, которая плевала на всю астрофизику и просто заставляла суптник лететь к цели, сжигая при этом топливо(занятный пример представлен ниже).
Этот naive chase отлично работал на небольших расстояниях. Совместив его с hohmann elliptic transfer, который к этому моменту я докрутил до состояния бета, у нас получилось решить все задачи из 3001-3004. Также я пытался сделать phasing для эллиптических орбит, однако из-за каких-то погрешностей точность phasingа оказалась меньше, чем нужно. Тем не менее, применение одной итерации phasing, а потом naive chase привело к увеличению очков.

День третий (29.06)

Весь день был проведен в попытках улучшить переходы по эллиптическим орбитам для 3001-3004, чтобы затем использовать в 4001-4004. Я попытался еще ускорить виртуальную машину посредством psyco (работало отлично, но только на 32-битных системах) и cython(работало везде, но компиляция была слишком долгой, кеширование скомпилированного нужно было делать, а профит был меньше, чем с psyco, т.о. в итоге от него отказались). В 13:04 был эпический "OMFG", когда мы заметили, что в Orbital Mechanics (описание ее смотрите ниже) есть матлабовский код, в котором есть готовые нужные нам формулы и алгоритмы - только копируй, исправляй и пользуйся. По этому коду xa4a переделал определение параметров орбиты, а я пытался сделать smart chase - через уравнение Ламберта. Однако, по-моему мнению, это было лишним - код нормально работать отказывался, а определение параметров орбиты вообще стало работать хуже, чем было. Smart chase также работал хуже, чем naive chase. Я попытался вывести hohmann elliptic delay - аналог hohmann delay но для произвольных эллиптических орбит, однако и этот алгоритм нам не очень пригодился. В этот момент - оставалось часа 3 до конца контеста - мы решили, что нужно хотя бы какие-нибудь Score получить на задачах 4001-4004. Murkt сделал простой солвер, который работал также, как 3001-3004(hohmann elliptic transfer+naive chase), однако ему не хватало топлива. Остальные три часа мы пытались подстроить naive chase, так чтобы он экономил топливо. Итог - пойманы три спутника в 4001(третий спутник был пойман мной за 7 минут до окончания, скриншот смотрите ниже) и два спутника в 4002.
Итог контеста - Weighted Total Score 2852.2285 (14 problems solved), 21 место, если последние 4 часа контеста все участники прохлаждались :)

О разном

  • Репозиторий с исходниками можно найти здесь
  • Orbital mechanics - кодовое название книги Howard Curtis "Orbital mechanics for engineering students" - для нас она стала Библией астрофизики
  • Отчёт от Murkt
  • Отчёт от xa4a

Организаторам

  • Слишком много версий заданий. Последние я даже не читал - надоело
  • Математика - это круто, и я не жалею, что участвовал в контесте, но всё же хотелось увидеть programming contest

Выводы

Как ни странно, текущий раздел не последний в этом отчёте. Тех, кто интересуется математикой, могут также прочитать и следующий раздел. Здесь же хотелось отметить, что фана от ICFPC 09 было всё же меньше, чем от Sapka (возможно потому, что Sapka была первым моим подобным контестом), однако море удовольствия от решения математических задачек я всё же получил. Будем надеяться, что в следующем году я тоже смогу поучаствовать.

О математике

За время контеста я получил(вывел сам, подсмотрел в Orbital Mechanics) множество формул. Здесь небольшой список, что было проделано(список не включает достижения остальных участников соревнования):









Кодовое имяОписание
hohmann delayПозволял найти время, которое нужно подождать на текущей орбите, чтобы при hohmann transfer на целевую орбиту попасть в нужную точку. Я выводил из равенства конечных углов, где конечные углы - функции времени. Этот hohmann delay не был использован в решении
phasingБыл подсмотрен в Orbital Mechanics и немного подправлен, чтобы была возможность совершать подстройку из любой точки орбиты, а не только из перигея. Вывод можно посмотреть в Orbital Mechanics
orbital equation v.1Позволял находить уравнение орбиты по двум точкам. Был выведен из системы двух уравнений орбиты в разных точках. Работал идеально на орбитах, apse line которых совпадала с осью абсцисс
orbital equation v.2К v.1 был добавлен еще один параметр - угол поворота орбиты. Параметры должны были находиться теперь уже по трём точкам. Решение искалось численно, потому что косинусы. Довести до ума так и не получилось, возможно, в моих предположениях была какая-то ошибка
orbital equation v.3Было использовано уравнение орбиты в векторах. Также должно было определять по трём точкам и находить угловой момент и вектор эксцентриситета. Не был доведен до ума, потому что см. ниже
orbital equation v.4Был подсмотрен в википедии в статье про кеплеровские орбиты. Вероятно, я где-то ошибся, но и эта формула выдавала неправильные результаты, хотя по этой же статье xa4a смог сделать рабочую версию
hohmann elliptic transferПочти то же самое, что и hohmann transfer, только без упрощений, возможных для круговых орбит. Вывод можно посмотреть в Orbital Mechanics. Было использовано для 3001-3004
elliptic phasingТо же самое, что и phasing, но для эллиптических орбит. Работало хуже, чем phasing, но работало для некоторых задач из 3001-3004. Вывод можно посмотреть в Orbital Mechanics
Smart chaseChasing maneuver по Orbital Mechanics. Работал не очень, использован не был

Wednesday, June 24, 2009

Django CouchDB backend 0.1

Не так давно стараниями 42cc была выпущена версия 0.1 бэкенда к CouchDB для Django ORM. Доступ к нему можно получитьна github, также есть трак. Так как я принимал в этом проекте весьма деятельное участие, то мне бы хотелось рассказать про него поподробнее.
Я не буду останавливаться на том, нужна или не нужна CouchDB. Вы можете прочитать об этом здесь или в google. Мне бы хотелось отметить один из недостатков CouchDB - он непривычен для людей, привыкших к реляционным базам данных, а следовательно, может показаться неудобным. Это и стало одной из причин разработки django-couchdb.
Основная цель разработки - позволить описывать взаимодействие приложений на Django с CouchDB на знакомом "языке" - на языке Django ORM. Для поверхностного использования не понадобится даже знания о том, что такое CouchDB - достаточно прописать backend в DATABASE_ENGINE и использовать его. Простейшие примеры того, что умеет django-couchdb(в виде теста):

class Boo(models.Model):
title = models.CharField(max_length=20)
slug = models.SlugField()
class Meta:
unique_together = ('title', 'slug')

class Foo(models.Model):
boo = models.ForeignKey(Boo)
boo2 = models.ForeignKey(Boo, related_name="foo2_set")

b1 = Boo(title="1", slug="1")
b1.save()
b11 = Boo(title="11", slug="1")
b11.save()
b2 = Boo(title="2", slug="2")
b2.save()
f1 = Foo(boo=b1)
f1.save()
f2 = Foo(boo=b2)
f2.save()
f3 = Foo(boo=b1,boo2=b2)
f3.save()
assert_equal(Foo.objects.filter(boo__title="1").count(), 2)
assert_equal(Foo.objects.filter(boo__title="11").count(), 0)
assert_equal(Foo.objects.filter(Q(boo__title="1") | Q(boo__slug="2")).count(), 3)

assert_equal(Foo.objects.filter(Q(boo__title="1") & Q(boo2__title="2")).count(), 1)
assert_equal(Foo.objects.filter(Q(boo__title="1") & Q(boo2__title="11")).count(), 0)

А именно: работает генерация "схемы" из моделей, insert, update, delete, select, joins(не все). Не работает - ManyToManyField, aggregates. Поэтому django.contrib(например, admin) работает, но не полностью.

Перспективы развития

Дальнейшее развитие будет вестись в нескольких направлениях:
  1. Исправление недостатков, поддержка других возможностей ORM
  2. Развитие backend для использования сильных сторон CouchDB
  3. Увеличение производительности

Выводы

Получился очень удобный инструмент, скрывающий особенности реализации (JS, HTTP запросы). Спасибо за внимание, ожидайте новые версии и новые возможности :)

Friday, June 12, 2009

Bachelor Thesis in Computer Science. Part 1

Finally, my bachelor thesis has come to an end! And now I am going to publish the most interesting stuff I've done. Let me introduce you CamTrack, a simple OpenCV based tool that can be used to control your desktop using webcam. There is nothing breaking new in there (it is only a bachelor thesis and in Ukraine it is not something difficult). But I think, it can be a strong open source competitor to the CamSpace software.





You can find the source code on bitbucket. Or use direct link to download latest version. Don't hesitate to ask if you have any problems with installation.

If you're interested in this software or if you want to take part in it - please contact me.

Announcement for russian readers: I am also going to publish my thesis papers and give some suggestions about writing thesis using LaTeX. Stay tuned.

Wednesday, May 13, 2009

О замыканиях в Python и С++

Предыдущий пост вызвал множество споров в pythonua@c.j.r на тему сложности синтаксиса С++ и сложности синтаксиса лямбд в частности. Действительно, в С++ 0x лямбды - это не one-line expressions, а полноценные анонимные функции. Именно поэтому синтаксис и так сложен.
Например, возьмём Python и его лямбды. Так как синтаксис Python изначально минималистичен, то и лямбды в нём выглядят аккуратно и понятно. Однако, с другой стороны такая минималистичность может сыграть и злую шутку. Вот пример - не так давно поднимался вопрос о замыканиях в Python. Речь в этой статье шла о таком небольшом сниппете:

l = []
for i in range(2):
for j in range(2):
l.append(lambda: i + j)
При такой реализации замыкания не происходит - в l мы получим абсолютно одинаковые функции. Основной вывод вышеприведенной статьи - правильная реализация замыканий в Python - это:
def lsum(n, m):
return lambda: n + m

l = []
for i in range(2):
for j in range(2):
l.append(lsum(i, j))
Но что мы видим в этом коде? Вместо удобного inline использования - отдельная функция, которая отвлекает внимание и всё портит.
Теперь вернёмся к С++ и посмотрим как в игру вступает один из синтаксических наворотов - capture list:
#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/function.hpp>

int main()
{
std::vector<boost::function <int()>> l;
for (int i=0;i<2;++i)
for (int j=0;j<2;++j)
l.push_back([i,j]{ return i + j; });
for_each(l.begin(),
l.end(),
[](boost::function <int()> f){ std::cout << f() << "\n"; });
}
Данная программа выводит:
0
1
1
2
Как и ожидалось. Таким образом, "синтаксический мусор" в С++ 0x - это по большей части нужные и полезные вещи. Спасибо за внимание!

C++ 0x уже сегодня?

Вступление

С++ 0x - кодовое имя грядущего стандарта С++, который несет в себе огромное количество изменений. Если и до этого язык С++ был одним из самых нагруженных и сложных, то с приходом нового стандарта он может вообще быть погребен под собственной массой. Но случится ли это? Ответу на этот вопрос и посвящен данный пост.

Основная часть

На BoostCon было точно подмечено, что "С++ 0x" нужно читать как "C++ 0A","C++0B", etc, однако многое можно попробовать уже сейчас.
Для этого я собрал gcc из cxx0x-lambdas-branch, который, по-моему, сейчас максимально близок к C++0x (бранч периодически мержится с основной веткой, но в этом бранче полностью отстутствуют Concepts). О том, как собирать - смотрите ссылки ниже.
Итак, имеем:
$ bin/gcc --version
gcc (GCC) 4.5.0 20090421 (experimental)
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Попробуем что-нибудь написать:
#include <iostream>
#include <vector>
#include <algorithm>


int main()
{
std::vector<int> v = {1,2,3,4,5};
std::vector<std::pair<int,int>> v2;
std::transform(v.begin(),
v.end(),
std::back_inserter(v2),
[](int x){return std::pair<int,int>(x, x*x);});
for_each(v2.begin(),v2.end(),[](std::pair<int,int> x){std::cout<<"("<<x.first<<","<<x.second<<")";});
int s = 0;
for_each(v.begin(),v.end(),[&s](int x){s+=x;});
std::cout<<"\nResult:"<<s<<"\n";
// static_assert(false, "Dummy assertion!");
auto is_42 = [](int x) {return x == 42;};
auto answer = 42;
if (is_42(answer))
{
std::cout<<"Yes, it is!\n";
}
decltype(answer + 42) new_answer = 43;//?
std::cout<<"New Answer:"<<new_answer<<"\n";
}
Впечатляет? Присмотритесь внимательней, в этом примере: initializer lists(список инициализации для v), right angle brackets (две правые угловые скобки в определении v2), lambda(с захватом переменных и без), static_assert (закомментирован), auto (для простых случаев и для лямбда-выражений), decltype(для определения типов). Меня очень впечатлило. Несмотря на то, что в язык добавлены новые элементы, язык стал только выразительней, а никак не сложнее.
Таким образом, С++ вбирает в себя всё новые и новые идеи. Несложно заметить, что С++ 0x - огромный шаг поближе к Haskell. Кто считает, что это чепуха - присмотритесь к Variadic Templates:
template<unsigned... Args> struct mysum;
template<>
struct mysum<> {
static const int value = 0;
};

template<unsigned x, unsigned... Args>
struct mysum<x, Args...> {
static const int value = x + mysum<Args...>::value;
};

int main()
{
static_assert(mysum<10,20,11,1>::value==42, "Wow!");
}
Variadic Templates уже доступны, однако работают не полностью.

Выводы

С++ 0x - попытка скачкообразно осовременить С++. И, как мне кажется, эта попытка вполне может оказаться удачной.

Ссылки

Спасибо за внимание!