6 апреля 2012 г.

Задачи на собеседованиях: оптимизация

Часто встречающаяся задача на оптимизацию.

Есть два шарика и небоскрёб в 100 этажей. Если бросать шарики из окон небоскрёба, то начиная с какого-то этажа они будут разбиваться вдребезги (и после этого разбившийся шарик уже нельзя будет использовать). Нужно предложить алгоритм, который поможет найти этот роковой этаж за оптимальное число итераций. Шарики не обязательно сохранять целыми к концу алгоритма.

26 марта 2012 г.

Проблемы переносимости: mutex

О проблемах переносимости приложений с использованием мьютексов. Мьютексы есть и в Windows и в POSIX, только работают они по-разному.

В Windows мьютексы позволяют выполнять рекурсивные локи из одного и того же потока без каких-либо deadlock'ов. В этом случае просто инкрементируется счетчик рекурсивных локов. Соответственно, чтобы освободить мьютекс для других потоков нужно будет столько же раз выполнить unlock(). Это их стандартное поведение, которое никак не изменить. Нужно просто учитывать это при дизайне алгоритмов приложения.

В POSIX мьютексы по-умолчанию не позволяют выполнять рекурсивные локи (т.е. второй вызов лока переведет поток в состояние ожидания), однако такое поведение может быть изменено путем изменения типа мьютекса. Возможно три варианта: 1) рекурсивные локи возможны (PTHREAD_MUTEX_RECURSIVE); 2) рекурсивные локи невозможны (PTHREAD_MUTEX_NORMAL); 3) режим errorcheck - рекурсивные локи невозможны, но вместо ожидания функция lock() возвращает ошибку (PTHREAD_MUTEX_ERRORCHECK).

Получается, что при необходимости портирования приложения с одной платформы на другую возможны некоторые проблемы. Например, при переходе с POSIX на Windows поведение PTHREAD_MUTEX_NORMAL невозможно, если не предусмотрено архитектурой. Как вариант, нужно будет заменить "родной" Windows-мьютекс на искусственный - можно использовать для этого семафор со значением 1, либо использовать какую-то стороннюю реализацию мьютекса, например из библиотеки Boost.

19 марта 2012 г.

Цитата: Фридман

Девочки, когда мне было столько лет, сколько вам, мои родители говорили: "Том, доедай свой завтрак - люди в Китае и Индии умирают с голоду". А сегодня я говорю вам: "Девочки, доделывайте ваше домашнее задание — люди в Китае и Индии умирают от желания получить вашу работу".
В современном Китае Билл Гейтс — это Бритни Спирс. В современной Америке Бритни Спирс — это Бритни Спирс. Вот, в чем наша проблема.
Томас Фридман,
"Плоский мир: краткая история XXI века"

11 марта 2012 г.

Бесплатная альтернатива valgrind под Windows

Бесплатная утилита с функциональностью memcheck — Dr.Memory. Я давно такую искал, наконец-то она появилась.

Работает под Windows и Linux. Запускается из консоли, довольно легко настраивается (например, на отсеивание ненужных срабатываний). Отслеживает утечки памяти, неправильное освобожение, чтение/запись в "неправильные" области и т.п.

При этом позиционируется как самый быстрый, уверяют, что обгоняет даже valgrind. Думаю, что коммерческим инструментам он всё же будет проигрывать, но пользоваться им вполне можно — пробовал сам под Windows, рекомендую.

Вообще хороший список таких альтернатив: http://stackoverflow.com/questions/413477/is-there-a-good-valgrind-substitute-for-windows

6 марта 2012 г.

Шовинизм от Майкрософт

Помню давным-давно в далекой далекой галактике в MS Visual C++ 6 был пункт меню "Generate Makefile", который делал возможным сборку C++-проектов без запуска IDE. Правда, Makefile использовал какие-то свои правила только частично похожие на обычные posixсовые, и вместо make был NMAKE. Но это мелочи.

Недавно обнаружил, что MS на этом не остановилась. Сейчас в VC++ 2010 еще пока есть NMAKE, но пункта генерирования makefile уже нет. Если хочется использовать makefile, то его нужно писать руками. Если есть нужда автоматизировать сборку проектов, то нужно пользоваться утилитой msbuild, которая идет в комплекте .Net Framework.

Отдельная утилита сбоки есть - и на том спасибо, как говорится. Да вот беда - чтобы собрать проект VC++ 7.0 при установленном .Net Framework 4, нужно либо открывать проект в IDE для конвертирования файла проекта в более новый формат, либо устанавливать более старый .Net Framework. Другого способа, кажется, нет.

Учитывая "нововведения" в последних версиях MSVS, то IDE скоро будет нужен только для сборки проекта.

UPD. В дополнение к .Net Framework еще нужно установить Microsoft Windows SDK с выбранными ".NET Development"->"Tools", "Visual C++ compilers" и "Intellisense and Reference Assemblies". Иначе проекты Visual C++ 2010 собираться скорее всего не будут.

26 декабря 2011 г.

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

Получение системной строки форматирования даты и/или времени:

  • В Linux:
    char *nl_langinfo(nl_item item);
    в параметре item задать значения D_T_FMT, D_FMT или T_FMT.
  • В Windows:
    int GetLocaleInfo(
      __in LCID Locale,
      __in LCTYPE LCType,
      __out LPTSTR lpLCData,
      __in int cchData);
    в параметре LCType задать значения LOCALE_SSHORTDATE, LOCALE_SSHORTDATE или LOCALE_SSHORTTIME.
P.S. Про кастомный парсер строки формата можно почитать здесь.

20 декабря 2011 г.

Code @ C++: Find sum of elements in the array

Code @ C++: Find sum of elements in the array: Наткнулся на задачу, которую предлагают в Yandex на собеседовании: Ниже приведены три варианта суммирования чисел с плавающей точкой (предполагается, что числа в массиве только положительные)...

21 октября 2011 г.

Как вернуться к интерфейсу Gnome Classic в Ubuntu 11.10

Новый интерфейс Ubuntu мне очень нравится, и он даже работает в режиме интеграции дисплея на VirtualBox. Но всё же на VirtualBox новый интерфейс подтормаживает, поэтому я вынужден вернуться на старый добрый Gnome Classic:

  1. Установить пакет gnome-session-fallback: sudo apt-get install gnome-session-fallback
  2. Выйти из текущей сессии (log out)
  3. На экране ввода пароля нужно в Настройках выбрать тип оболочки Gnome Classic
  4. Ввести пароль и выполнить вход. Теперь Ubuntu будет использовать Gnome Classic по умолчанию.

Источник: http://www.upubuntu.com/2011/10/how-to-use-gnome-classic-on-ubuntu-1110.html

19 сентября 2011 г.

Выключение компрессии в OpenSSL 0.9.8

В OpenSSL реализована встроенная поддержка zlib и по умолчанию включена компрессия пересылаемых пакетов данных. Причем при сжатии очередного пакета используется информация об уже сжатых и отосланых пакетах. Например, если вы отсылаете три одинаковых пакета по 400 байт, и первый пакет сжимается до 275 байт, то второй и третий пакеты в сжатом виде займут всего по 12 байт. Такой подход очень экономит трафик, но применим только к протоколу TLS, потому что он гарантирует доставку пакетов и сохраняет последовательность, в которой пакеты были отосланы. Понятно, что для протоколов с негарантированной доставкой такой подход к сжатию не может быть применён. Это подтверждает RFC 3749 "TLS Compression Methods", в котором написано следующее:

Some compression methods have the ability to maintain state/history information when compressing and decompressing packet payloads. The compression history allows a higher compression ratio to be achieved on a stream as compared to per-packet compression, but maintaining a history across packets implies that a packet might contain data needed to completely decompress data contained in a different packet. History maintenance thus requires both a reliable link and sequenced packet delivery.

Протокол DTLS, выбранный для нашего проекта, был разработан специально для UDP и не гарантирует доставку пакетов. Но большей частью он основан на TLS, и по идее реализация протокола DTLS должна учитывать требования RFC 3749.

К сожалению, в OpenSSL этот момент не учитывается и в DTLS сжатие используется точно также, как и в TLS. Совершенно очевидно, что при потере одного из пакетов вся передача будет нарушена, и даже если этот пакет придет позже, то восстановить исходную последовательность принимающая сторона уже не сможет. Такой неприятный момент должен решаться простым отключением компрессии при использовании DTLS, однако в OpenSSL 0.9.8 это превращается в проблему, потому что в API просто нет функции отключающей компрессию. Вот цитата из документации на метод SSL_COMP_add_compression_method:

An OpenSSL server will match the identifiers listed by a client against its own compression methods and will unconditionally activate compression when a matching identifier is found. There is no way to restrict the list of compression methods supported on a per connection basis.

Начиная с OpenSSL 1.0.0 добавлена опция SSL_OP_NO_COMPRESSION, которая делает то, что мне нужно, но в версии 0.9.8 ее нет. В поисках решения я порылся в сети, но ничего внятного, кроме вот этого не нашел:

void disable_openssl_compression()
{
  STACK_OF(SSL_COMP)* comp_methods = SSL_COMP_get_compression_methods();
  sk_SSL_COMP_zero(comp_methods);
}

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

Между тем, изучив исходный код OpenSSL, я обнаружил, что информация о доступных компрессорах хранится в простом списке, в который можно добавлять свои компрессоры с помощью функции SSL_COMP_add_compression_method. А раз можно добавлять, то значит должен быть способ и удалять элементы списка. Такой функции в OpenSSL API нет, но после изучения исходников функции SSL_COMP_add_compression_method можно написать свою, не пользуясь хаками, а только предоставленным API:

void disable_openssl_compression (void)
{
  int n = sk_SSL_COMP_num(SSL_COMP_get_compression_methods());
  for (int j = 0; j < n; ++j)
  {
    SSL_COMP *comp = sk_SSL_COMP_pop(SSL_COMP_get_compression_methods());
    OPENSSL_free(comp);
  }
}

12 сентября 2011 г.

Этот загадочный SecurityError (Flex 3)

После очередной порции изменений моё Flash-приложение, загруженное на сервер, перестало работать. То есть не совсем перестало, потому что по click-событиям выполнялось то, что нужно, но визуальные элементы вообще перестали хоть как-то отображаться — кнопки не хайлайтились, текст не скроллился, картинки не отображались... Сложилось такое ощущение, что перестали обрабатываться сообщения, отвечающие за отображение компонентов.

Из-за того, что часть приложения продолжала функционировать, я даже не сразу заметил проблему. При этом локально всё замечательно продолжало работать. Я довольно долго тупил, думая, что это проблемы совместимости установленных Flash-плееров в браузерах и в системе. Но через какое-то время понял, что такое различие в поведении локального приложения и приложения на сервере может вызвать SecurityError. Только где? (Тут я в очередной раз порадовался своей привычке делать маленькие, логически законченные коммиты. Локализовывать баг при таком подходе одно удовольствие и всего лишь вопрос времени.)

Причина обнаружилась в установленном smoothBitmapContent="true" для одного из тегов mx:Image. Так как картинка для тега загружалась не с сервера приложения, то контент по определению считался небезопасным. Доступ к битовым данным в этом случае вызовет SecurityError, а smoothBitmapContent="true" как раз требует такого доступа. Это изменение попало в репозиторий совершенно случайно, в результате неудавшихся экспериментов, но оказалось фатальным для приложения.

Для меня остается загадкой, почему SecurityError вызвал именно такое поведение, когда одна половина приложения работает, а другая половина — нет.