суббота, 8 августа 2009 г.

[comp.prog.cpp] Несколько слов об исключениях

Будучи еще не очень матерым блогером, я с интересом читаю чужие блоги, на которые попадаю совершенно случайно и в которых сразу же вижу интересные материалы. Пока я наблюдал две тенденции – либо мне нравится то, что пишет человек в блоге, либо не нравится. Но иногда происходят и странные вещи – что-то из написанного в блоге кажется мне здравым и разумным, а что-то – совершенно наоборот. Поводом к написанию данной заметки оказался недавний пост Exceptions and Goto в блоге bishop-it. Мне казалось, что об исключениях, кодах возврата и goto в C++ было сказано уже столько разумных и правильных слов, что подобных сравнений exceptions и goto не должно возникать в принципе. Однако ж.

Так вот об исключениях. Я совсем не гуру в C++ и не сенсей исключений. Но почему бы и мне не сказать о них пару слов? Путь банальных и повторяющих идеи гораздо более умных C++ных монстров (включая Страуструпа, Саттера, Александреску и пр.). Может я сам через год-другой перечитаю их и в очередной раз удивлюсь тому, каким я был наивным и неопытным.

Итак, в языках C++ного семейства (C++, Java, C#) есть всего три основных способа информирования об ошибках:

  1. Возврат признака успешности/неуспешности операции и, в случае неудачи, помещение кода ошибки в какую-то глобальную переменную. В C – это errno, а функции в случае ошибки возвращают отрицательные значения или NULL. В ACE, например, принято возвращать –1 в случае ошибки и выставлять errno. В WinAPI многие функции возвращают FALSE или NULL, а код ошибки доступен через GetLastError().
  2. Возврат кода ошибки функцией. Т.е. все функции конкретной библиотеки возвращают, скажем, int или какой-то специальный тип. По возвращенному значению можно определить, успешно выполнена функция или неудачно. А если неудачно, то в чем причина. Без каких-либо глобальных переменных, просто по возвращенному значению. Такой подход, к примеру, использован в нашем SObjectizer.
  3. В случае ошибки порождаются исключения.

Из всех этих способов самым дебильным и неудобным является первый способ. Причем, он неудобен как в использовании библиотек, применяющих такой стиль, так и при написании собственных библиотек в подобном стиле. Здесь проблема сидит на проблеме. Глобальные переменные должны быть thread safe и, более того, thread specific. Код возврата функции можно проигнорировать, а глобальный флаг можно не проверить. Во время обработки ошибки можно вызвать какую-то вспомогательную функцию, которая занулит errno. И т.д. и т.п.

Второй способ гораздо лучше и удобнее. И в принципе, если по каким-то причинам использование исключений нежелательно, этот способ и должен использоваться на практике.

Но все равно и у первого, и у второго способа есть три фатальных недостатка:

  • эти подходы не могут использоваться с перегруженными операторами;
  • программа может легко проигнорировать возникшую ошибку. Достаточно просто забыть проверить код возврата. Или написать проверку неправильно;
  • программирование с использованием подобных подходов приводит к написанию функций в виде широкой “лесенки” из if-ов. Что пагубно сказывается на читабельности и сопровождабельности программы.

Всех этих недостатков лишены исключения. Поэтому лично я предпочитаю их остальным способам информирования об ошибках.

Нужно сказать, что мой путь к восприятию исключений был долгим и извилистым. Сначала у меня не было возможности использовать исключения, т.к. их поддержка появилась где-то в Borland C++ 4.*. Потом возможность появилась и я начал ей пользоваться. Наверное, с изрядным over use. Поскольку где-то в районе 2000-2001 годов я старался их не использовать (следствием чего как раз и стали коды ошибок вместо исключений в SObjectizer). Может в этом свою роль сыграл и небольшой опыт программирования на Java, в которых исключения довели до маразма. Но в конце-концов я опять вернулся к тому, что исключения это отличная штука и им нужно отдавать предпочтение.

Почему я в итоге пришел к такому выводу? Потому, что прерывание программы, которая столкнулась с непредвиденной ситуацией, гораздо лучше продолжения ее работы в неправильном (неопределенном) состоянии. Я пробовал отказаться от исключений чтобы писать “надежный” софт. Но оказалось, что исключения способствуют надежности гораздо больше, чем коды ошибок и “простой” код из множества if-ов лесенкой. Не в последнюю роль в этом деле сыграло знакомство с принципом “fail fast, restart quickly”. Но это уже совсем другая история.

Но если исключения так хороши, то почему на них так упорно катят бочки? Потому, что программирование с исключениями может требовать от программиста больше внимания и усилий (и иногда требует, что характерно). Исключения могут возникать, по сути, в любом месте и оставлять программу в несогласованном состоянии. Скажем, мы обрабатываем подключение нового клиента к серверу и нужно сохранить описание этого клиента в трех словарях. Успешно проделали вставку описания в первые два, а на третьей вставке возникло исключение. Вот и все, появилась несогласованность. Что в этой ситуации делать? Возможных ответа здесь два.

Первый ответ сложный. Он касается гарантий кода по отношению к исключениям (т.н. basic и strong гарантии). По хорошему, нужно изъять описание клиента из двух первых словарей. Т.е. фрагмент кода по обработке подключения нового клиента должен быть написан так, чтобы контролировать, какие словари были модифицированы. А затем, при возникновении исключения, отменить эти модификации. Звучит уже сложно, не правда ли? В реальности ситуация еще хуже. Нужно обладать изрядной долей параноидальности и педантичности, а так же большим опытом, чтобы анализировать свой код на предмет наличия потенциально опасных в случае возникновения исключения мест. Поэтому написание кода, обеспечивающего strong-гарантии, действительно трудоемко и черевато ошибками.

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

Хитрость заключается в том, что ни первый, ни второй ответы не являются однозначно верными. Каждый из них имеет право на жизнь в определенных ситуациях и не имеет такого права в иных ситуациях. Например, при написании универсальных и общеупотребительных библиотек предпочтение следует отдавать первому способу. Ведь будет неприятно узнать, например, что std::vector.push_back оставляет весь вектор в несогласованном состоянии при возникновении bad_alloc. С другой стороны, при написании сложного прикладного кода обеспечение даже basic-гарантий может привести к столь сложному коду, что ни завершить его в срок, ни поддерживать в дальнейшем, не будет никакой возможности.

Итак, программирование с исключениями может быть сложным. Однако, есть уже устоявшиеся приемы, которые облегчают разработчикам жизнь. Как то:

  • использование иерархий классов-исключений, вершиной которой является std::exception (или std::runtime_error/logic_error). Используемая вами библиотека может иметь сотни/тысячи классов конкретных исключений, но это не будет вас волновать, если в вершине будет какой-то один класс, да еще производный от std::exception;
  • отказ от перехвата всех исключений и, особенно, от их подавления. Перехватывать нужно только те исключения, которые мы ожидаем в конкретном месте по конкретным причинам. И для которых мы знаем, как исправить ситуацию. Если мы не ждем чего-то и не знаем, что с этим делать – то лучше исключение не ловить вообще. Ну либо перехватить, залогировать, и отправить дальше. Ну и совсем в крайнем случае можно перехватывать исключения, чтобы оборачивать их в собственный тип исключения (это прием не очень хорош для C++, зато отлично работает в языках со сборкой мусора);
  • разделение функций/методов по принципу command (выполняет действие) и query (делает запрос). Методы-command-ы способны генерировать исключения и пользователь к этому готов. Тогда как методы-query, как правило, не бросают исключений, а информируют об успешности своего завершения с помощью кодов возврата.

Много проблем может доставлять неуёмное и непродуманное использование исключений в коде. Когда любой метод генерирует исключения на любой чих. Или же когда код завален try/catch-ами по самое нехочу. Но тут уж нужен опыт и здравый смысл. К сожалению, если речь идет о программистах вообще, то здравый смысл оказывается редким явлением. Чему свидетельством обилие говнокода, в который мы все рано или поздно вляпываемся (наверное потому, что сами его периодически и производим). Говнокод лучше переписывать. Или менять место работы, чтобы попасть в лучшие условия. Но, в любом случае, исключения здесь не причем – кривые руки способны превратить в кошмар любую здравую идею.

Вот, вкратце, что я хотел сказать по поводу исключений.

Disclaimer. Я не в состоянии в блог-посте затронуть другие аспекты этой проблемы. Как то: условия, к которых исключения использовать нельзя (real-time и embedded), интеграция с кодом на других языках, интеграция со сторонним бинарным кодом (от простейших LoadLibrary/dlopen и GetProcAddress/dlsym до таких порождений злого гения, как COM).

PS. Однако, осторожно нужно на кнопки в Windows Live Writer нажимать. А то черновики в один клик публикуются :)

пятница, 7 августа 2009 г.

[comp.flame] Маразм крепчал: производительность получилась несколько более низкой

Зашел сегодня на linux.org.ru, увидел новость о переписывании кода SQLite на C#/Mono. Прочитал и охуеофигел:

Noah Hart осуществил порт Sqlite на C# путем переписывания оригинальной библиотеки строчка за строчкой. При этом производительность получилась несколько более низкой, чем у оригинального кода - порт примерно в 5 раз медленнее (это означает, что он способен обработать примерно 1.5 миллионов операций типа SELECT или 300 000 операций типа INSERT за секунду - очень достойные результаты).

Блин, хочу жить в мире, где пятикратные тормоза называются “несколько более низкой” производительностью! Может для этого достаточно стать C#/Mono программистом? Тогда все заботы по написанию производительного кода, по профилированию и оптимизации программ уйдут сами собой. Будет всего лишь несколько более низкая производительность и все. Лепота!!!

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

The frequently asked questions on the web site talks about performance (it is about 5 times slower than native code, but still, it can do 1.5 million selects per second, 300k inserts per second).

Т.е. да, производительность сосет по полной, но тем не менее 1.5M select-ов и 300k insert-ов в секунду могёт.

PS. Кстати интересно, на каких-таких insert-ах SQLite показывает 300k/sec? Я год назад запарился на SQLite делать поддержку 20K транзакций в секунду, поскольку как только в SQLite появляется транзакция, то производительность падает на порядки. Пришлось объединять сотни вставок в большие транзакции, чтобы минимизировать их количество. Или же они считают количество insert-ов в одной транзакции? Или вообще в одной транзакции зафиксировали 10K записей, экстраполировали результат и получили 300k?

четверг, 6 августа 2009 г.

[comp.flame] Если бы программисты ставили себя на место пользователей…

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

На мой взгляд, такие замечания неизбежны. Нельзя разрабатывать программу так, чтобы их не было совсем. Что-то теряется на этапе постановки задачи, что-то упускается на этапе реализации, о чем-то вообще было забыто, что-то появилось уже после начала работ. Так что от “тщательной доработки напильником” не уйти. Но вот сократить объем этой работы можно, иногда очень серьезно. Что для этого нужно?

Нужно, чтобы программист ставил себя на место пользователя.

Вот такой вот несложный, на первый взгляд, рецепт. Когда разработчик становится пользователем своей программы, он начинает сам замечать, что “Эта операция используется достаточно часто и доступ к ней хорошо бы иметь на toolbar-е и через горячую клавишу”, “Две эти операции, как правило выполняются одна после другой, но им назначены такие горячие клавиши, что задействовать их одной рукой не получается”, “Было бы хорошо, если бы мне не приходилось обязательно задавать вот эти три параметра, лучше чтобы по умолчанию для них использовались значения вот этого четвертого параметра”, “Вот для таких операций в логе и в мониторинговой панели хотелось бы видеть следующие показатели…” и т.д., и т.п.

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

А почему же не удается ставить себя на место пользователя? С ходу я могу назвать две причины.

Первая причина в том, что у программистов и пользователей разные цели. Пользователь хочет с помощью программы решать свои задачи. А программисту нужно написать программу (обычно “еще вчера”, а иногда и “еще позавчера”). Поэтому программисту важно минимизировать собственные усилия, сделать так, как проще ему. Хорошо, если в рамках того, о чем просил пользователь :) В итоге получается, что простота реализации для программиста не есть простота использования для пользователя. Как следствие – замечания, переделки и доработки.

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

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

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

У нас в компании решили провести небольшой фотоконкурс. Правила конкурса рассылал Слава Костин, в недавнем прошлом отличный программист, а сейчас замечательный менеджер и страстно увлеченный фотолюбитель. Наверное именно поэтому одно из правил конкурса гласит:

* фотографии должны быть предоставлены в цветовых координатах RGB, в профиле sRGB, обязательно в 8-битном цвете, иметь разрешение 300dpi, размер по длинной стороне минимум 2568 пикселей. Рекомендуемый (но не обязательный) размер 2413x3626 пикселей. Размер файла фотографии должен быть не менее 16 КВ, формат JPG, TIFF (в формате PC) или BMP. Допускается участие в конкурсе изображений, отсканированных с бумажных носителей или негативов.

Из того, что здесь перечислено, я понял разве что половину. Поскольку я не фотолюбитель. Ну мне нравится хорошие фотографии. В смысле посмотреть. Иногда я даже сам снимаю цифровой мыльницей. Но по принципу моего друга: “Если есть пейзаж, то почему бы его не снять?”. И выдержку от диафрагмы я отличаю лишь потому, что отец у меня был заядлым фотолюбителем, и в детстве я часто слышал эти слова.

Итак ситуация – у меня есть несколько любительских снимков, которые можно было бы отдать на конкурс. Но как хотя бы понять, подходят ли они по техническим параметрам? Можно поставить Photoshop и сделать в нем необходимые преобразования (если они вообще необходимы). Но что такое Photoshop и с чем его едят? ;)

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

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

Так что, уважаемые программисты, дочитавшие до этого места: чаще пробуйте быть в шкуре пользователей своих программ.

Disclaimer. Я знаю, что есть отдельное понятие usabillity. И что в серьезных проектах (типа MS Office) этим делом занимаются специальные люди. Но если usabillity пользовательского интерфейса – это еще более-менее хорошо освещенная тема, то удобство использования, скажем, server-side приложений, рассматривается не так широко. Хотя там непаханое поле работы. Особенно в небольших проектах, которые делают маленькие команды, в сжатые сроки, с небольшими бюджетами, с прямым общением между разработчиками и пользователями…

PS. На тему usabillity: сегодня наткнулся на интересную презентацию -- The Keyhole Problem

среда, 5 августа 2009 г.

[comp.prog.d] Стал доступен фрагмент книги А.Александреску “The D Programming Language”

"D"iving Into the D Programming Language

PS. В своем анонсе Андрей Александреску сказал, что хотел бы переписать этот фрагмент.

PPS. Мне понравилось приведенное там высказывание Алекса Степанова (касательно поддержки обобщенного программирования в языке):

These are my litmus tests: if a language allows me to implement max and swap and linear search generically—then it has some potential.

[comp.prog.cpp] Маленькое изобретение – проверка корректности аргументов в конструкторе

Наверняка все уже украденоизобретено до нас, но я раньше такого не видел. Поэтому порадовался, когда до этого додумался. Итак, конструктор какого-то C++ класса получает набор аргументов. На аргументы накладываются некоторые требования. Нужно порождать исключение в конструкторе, если пользователь не выполняет эти требования. Как это обычно делается? Наверное, как-то вот так:

class some_class_t {
  private :
    // Имя должно состоять из определенного набора символов и
    // длина имени должна быть не меньше N и не больше M.
    const std::string m_name;
    // Час, должен быть в диапазоне [0..23].
    const unsigned int m_hour;
    // Минута, должна быть в диапазоне [0..59].
    const unsigned int m_minute;
  ...
  public :
    // Вот и конструктор.
    some_class_t(
      const std::string & name,
      unsigned int hour,
      unsigned int minute )
      // Вот это важно! Сначала атрибуты получают значения...
      : m_name( name )
      , m_hour( hour )
      , m_minute( minute )
      {
        // ...а уже потом выполняется проверка.
        ensure_name_validity( m_name );
        ensure_hour_validity( m_hour );
        ensure_minute_validity( m_minute );
        ...
      }
  ...
};

Что здесь нехорошо? А то, что если инициализация аргументов дорогая, то мы сначала платим цену инициализации, а только затем начинаем делать проверки. Но ведь ее можно и не платить ;) Вот так:

// Вот такие валидаторы нам нужны:
const std::string &
ensure_name_validity( const std::string & arg ) { ...; return arg; }

unsigned int
ensure_hour_validity( unsigned int arg ) { ...; return arg; }

unsigned int
ensure_minute_validity( unsigned int arg ) { ...; return arg; }

class some_class_t {
  ...
  public :
    // Вот и конструктор.
    some_class_t(
      const std::string & name,
      unsigned int hour,
      unsigned int minute )
      // Вот это важно! Параметры проверяются до
      // инициализации атрибутов.
      : m_name( ensure_name_validity( name ) )
      , m_hour( ensure_hour_validity( hour ) )
      , m_minute( ensure_minute_validity( minute ) )
      {
        ...
      }
  ...
};

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

А еще эту идею можно развить до явной декларации требований к атрибутам. Вот, допустим, так можно явно прописать в коде, какие требования налагаются на параметр name конструктора нашего демонстрационного класса:

// Валидатор параметра name.
struct valid_name_t {
  const std::string & m_name;
  valid_name_t( const std::string & name )
    : m_name( ensure_name_validity( name ) )
    {}
};

// Теперь требование к аргументу name может быть указано
// прямо в описании конструктора.
class some_class_t {
  public :
   some_class_t(
     const valid_name_t & name,
     ... )
     : m_name( name )
     ...
};

По большому счету, развивать этот подход можно и дальше. Таким образом можно прописывать требования не только к параметрам конструктора, но и аргументам обычных функций/методов. В общем, если где-то нужно применять defensive programming, то есть с чем поиграться.

PS. Хочу поделиться маленькой радостью :) Количество загрузок Mxx_ru со страницы RubyForge достигло круглого числа 1400! Если учесть еще и количество загрузок через RubyGems, то получается, что Mxx_ru скачали больше 2100 раз. Что не может не радовать (хотя, смотря с чем сравнивать ;). Так что есть повод еще раз сказать спасибо всем пользователям Mxx_ru: спасибо большое!

вторник, 4 августа 2009 г.

[life] О высоких девушках

Говорят, что большие любят маленьких и наоборот. Но это точно не мой случай :) Мне нравятся высокие девушки. Коих в моем родном городе не так уж и много. Из-за чего у меня еще со времен полового созревания выработался рефлекс – все молодые особы женского пола от 170 см. автоматически привлекают мое внимание. А в районе 180 и выше – мое очень пристальное внимание :))

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

Поэтому огромное уважение я испытываю к девушкам, которые при своем высоченном росте решаются еще и на высокие каблуки. Вот буквально полчаса видел одну такую в банке. Сама хорошо за 185, на каблуках, да еще с высокой прической. Такое впечатление, что со всем этим она была ну никак не меньше двух метров. Я даже на нее снизу вверх смотрел ;) Вот это молодца! Респект и уважуха!

[comp.flame] Correct by construction говорите?

Многие верят, что существуют или же могут быть придуманы методики и приемы разработки качественного софта. На просторах программистких форумов апологеты функционального подхода пытаются доказывать это неудобочитаемыми фрагментами Haskell-ового кода, твердя одну и ту же мантру: “Написанная однажды корректная чистая функция остается корректной всегда”. Методологи разработки ПО не останавливаются и плодят все новые методологии. Вот и сейчас Айвар Якобсон заявляет, что создает новую теорию, которая должна позволить создавать great software. Блажен, кто верует, однако.

Почему я так скептически отношусь к этому? Да потому, что никто не принимает в расчет внезапных “затмений”, которые случаются у разработчиков. Но которые оказывают очень и очень сильное влияние на качество кода.

В качестве примера приведу фрагмент кода, который мой коллега вчера раскопал в недрах переписываемого нашей командой проекта:

unsigned long f( unsigned long a ) {
 while( (1 << 24) < a )
  a -= (1 << 24);
 return a;
}

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

Наша версия – оставить в числе только младшие 24-ре бита. Т.е занулить 8-мь старших бит в 32-х битовом числе. Зачем для этого пришлось писать цикл с вычитанием и нельзя было воспользоваться обычным битовым AND-ом (ну или хотя бы остатком от деления) – тайна сия велика есть.

Еще большая загадка для меня – кто написал это. И в каком состоянии он находился, когда писал этот код. Ведь у нас в компании практически не было безмозглых программистов, особенно в самом начале (когда этот фрагмент и был написан). Но факт остается фактом – кто-то из нормальных программистов откровенно ступил и написал вот такую глупость.

Может ли любой хороший программист совершить подобный ляп программируя даже на самом супер-пупер функциональном языке в рамках самой мега-супер-пупер методологии? Да запросто!

PS. А ведь бывает и более сурово. Когда ступор настигает тебя не при написании кода, а еще при разработке алгоритма. Вчера я у себя нашел очень серьезный баг. В процедуре выборке новых строк из БД и распределении их по заинтересованным в них клиентам я допустил ошибку из-за которой часть строк могла быть помечена как обработанные, но не попасть к клиентам. Причем ошибка была именно в алгоритме и мне повезло на нее наткнуться, поскольку счастливым для меня образом в одном из тестов совпали нужные количества строк, клиентов и размеры пакетов для клиентов. А если бы не совпали? Пришлось бы кому-нибудь лет через шесть приводить в своем блоге облагороженный кусок моего кода с вопросом “какой кретин эту байду написал и в каком он был состоянии?!”… :(

Update. После публикации этого фрагмента на govnokod.ru в нем выяснилась интересная особенность. Если младшие три байта числа отличны от нуля, то он действительно обнуляет старшие байты (например: 0x3000001 -> 0x2000001, 0x1000001, 0x000001). А вот если три младших байта равны нулю (т.е. число кратно 1^24), то оно сокращается до 1^24 (например: 0x3000000 -> 0x2000000, 0x1000000). Она как! :)

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

[life] Авторы логотипов: как они это делают?

Есть две задачи, к которым я иногда подступался, но всегда чувствовал, что мои мозги с ними просто-напросто не справляются. Первая задача – это выдумывание названий. Например, названий проектов. Лучшее, из того, что у меня получалось – Objessty, Gemont, Mobilling, MxxRu – все это аббревиатуры, это еще хоть как-то перебором получается. Но вот вторая задача – создание логотипа – это полный трындец. Тогда как у некоторых это получается:

Как они это делают? Например, вот это меня просто порвало:

Внушаить!

Update. Нашел еще одну подборку логотипов: здесь 45 штук, например:

В тему так же сайтик Logo Of The Day и его рубрика Logo Of The Month. Например, вот логотип июля 2009:

[life.photo] Практически живопись белым по белому

Номер раз (Сергей Румянцев, “Один, совсем один”):

Номер два (Фото из National Geographic, отсюда (via umabarzy)):

Номер три, самый самый (Алексей Масанов, “нежная карточка про младого спиногрыза”):

воскресенье, 2 августа 2009 г.

[comp] JRuby начинает дышать на Android-е

Любителям Ruby (и пользователям JRuby в частности) должно быть интересно: Return of Ruboto! (один из разработчиков JRuby потихонечку портирует JRuby на Android-ы):

[attention] Вирусы атакуют?

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

Я в растерянности. Проверил блог с помощью Dr.Web – ничего не найдено. Сталкивается ли еще кто-нибудь с подобным фокусом? Если это вирус, то как с ним бороться?

Update. Такое сообщение возникает при обращении к нескольким картинкам в посте от 29 июля сего года. Так что проблема с хостером этих картинок. Я их пока сотру.

[comp.concurrency; business] Бабах: Intel поглощает CilkArts

Вот такие вести с полей:

Cilk Arts, developer of the Cilk++ platform which lets C++ developers more easily exploit multicore processors, has joined Intel. Intel acquired the entire development team and all of Cilk Arts products and technology.

According to Intel's James Reinders, Cilk technology will complement tools such as OpenMP and Intel Threading Building Blocks. Over the coming weeks, Cilk++ will be integrated into Intel parallel tools like Intel Parallel Studio.

Флагманским продуктом CilkArts является Cilk++ – расширение языка C++ (соответствующий компилятор плюс рантайм плюс вспомогательные инструменты) для высокопроизводительных и параллельных вычислений. Разработчик вставляет в свою C++ программу специальные ключевые слова для распараллеливания и объединения задач, а все остальное за него делает Cilk++. Например, вот так выглядит реализованный с помощью Cilk++ подсчет чисел Фибоначчи:

int fib (int n) {
  if (n<2) return (n);
  else {
    int x,y;
    x = cilk_spawn fib(n-1);
    y = fib(n-2);
    cilk_sync;
    return (x+y);
    }
}

По большому счету мне все равно, кто будет владеть Cilk++ – я этим продуктом не пользуюсь, поскольку не занимаюсь HPC. Но сам факт того, что Intel сосредотачивает под своим крылом разные инструменты для параллельного программирования (OpenMP, TBB, Cilk++), говорит о том, насколько Intel заинтересован в максимальном использовании возможностей своих multi-core процессоров широкими массами. Т.е. multi-core – это всерьез и надолго, если кто еще не понял.

Может быть горячей темой следующих 5-10 лет будет multi-core программирование (как лет 15 назад было desktop-, а последние лет 10 – web-программирование)?  Имхо, это веселее, чем Web :)

И в любом случае – респект разработчикам Cilk-а. Надеюсь, поглощение произошло на выгодных для них условиях ;)