четверг, 31 января 2013 г.

Garbage collection PHP vs CRuby vs Go

    Раньше меня интересовало - а есть ли вообще сборщик мусора в PHP. Нет, я понимал что он должен был быть где-то - но он настолько херово работал до недавнего времени - что казалось что его вообще нет.  Нормально он заработал только после версии 5.3 когда он научился определять циклические ссылки. Вообще странно что мало кто пишет об этом очень важном для практического программирования нововведении. О позднем статическом связывании и замыканиях разве что ленивый не написал, а об этом ни слова. Хотя может я не там читал. Ради интереса прочитал статью про сборщик мусора в CRuby:
    Rare Are GC Talks   - на английском, но написана очень простым языком. Еще один плюс этой статьи - она объясняет все базовые алгоритмы сборки мусора на простых примерах. Если лень читать - то в кратце:
1. Ruby используется алгоритм mark & sweep
2. Они рассматривают 3 варианта новых сборщиков мусора
Для сравнения:
     В PHP используется алгоритм подсчета ссылок(Основы подсчета ссылок) - с дополнительным алгоритмом определения циклических ссылок (Сбор циклических ссылок)
     В Go также используется алгоритм mark & sweep, но в параллельном варианте. Еще одной особенностью его реализации является то что он совершенно не оказывает влияния на производительность если программа не выделяет память.

среда, 30 января 2013 г.

Паттерн observer(наблюдатель)

    В последннее время меня все чаще посещают мысли что паттерн observer(наблюдатель) - на самом деле является большим злом для архитектуры большинства систем. Если смотреть на него с теоретической точки зрения - одни плюсы. Взять хотябы его реализацию в магенте: там практически вся архитектура подключаемых модулей на этом паттерне построена. Нужно тебе выполнять какое-то действие при регистрации клиента - повесил обработчик на это событие и можеш быть(теоретически!) быть уверенным что тебя вызовут когда нужно. Можно фактически внедрятся в ядро системы и изменять ее поведение не меняя физически ни одного файла. Процесс установки модулей, который в других e-commerce системах зачастую наполнен шаманством с примением patch/diff и обязательным просмотром всех сделанных измений в магенте сводится к простому распаковыванию файлов в нужные места. Но за гибкость приходится платить.
        Когда дело доходит до практического применения это же преимущество становится и большим недостатком:
    1. Когда ты видеш вызов диспетчера какого-то события  - тебе довольно трудно понять что  действительно произойдет в этом месте кода. Никакая, даже самая умная IDE не сможет найти тебе те обработчики которые будут вызваны в этот момент. Тебе приходится самому искать их с помощью grep.
    2. Вот нашел ты эти обработчики, а дальше что ? в общем случае ты не имеешь понятия в каком порядке они будут вызваны. И будут ли вызваны вообще.
    3. Пол беды если эти обработчики написаны хорошими программистами. В этом случае ты можеш расчитывать что они не будут изменять ничего кроме тех объектов которые были переданы им вместе с событием. Но на практике часто случается что во время исполнения этих обработчиков меняется глобальное состояние систмы - либо явно через изменение каких -либо глобальных объектов либо неявно - через обращение к методам какого-нибуть синглтона. В результате мы получаем кучу side-эффектов. По сути ты вообще не знаешь что ждать после вызова диспетчера события.

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

Оптимизация Sphinx

      После прочтения статьи по оптимизации Sphinx:
Sphinx search performance optimization: attribute-based filters
Решил проверить это на практике - перевел поиск по категории в полнотекстовый поиск, вместо поиска по MVA аттрибуту. Результаты оказались весьма заметны:
В среднем согласно NewRelic время запросов к Sphinx уменьшилось на треть.  Это при том что у меня в индексе всего 20 000 продуктов. Если бы продуктов было 100 K+ - думаю разница была бы еще заметней.
Еще одна полезная статья по оптимизации Sphinx:
http://www.mysqlperformanceblog.com/2013/01/16/sphinx-search-performance-optimization-multi-threaded-search/

среда, 23 января 2013 г.

Внутреннее устройство Go

     Недавно прочитал две очень интересные статьи про то как в Go реализованы различные типы данных:
Если уровень английского позволяет, лучше прочитать их в оригинале - если нет, то я перескажу в кратце:
1. Все простые типы данных - int, float и тд занимают ровно столько же места сколько они занимают в Си. 
2. Массив в Go - это тоже самое что и массив в Си - никаких накладных расходов. То есть массив из 10 значений типа int будет занимать в памяти ровно 4 * 10 = 40 байт. И не байта больше.
3. То же самое относится и к структурам - размер памяти требуемый для размещения структуры - это сумма размеров ее членов и не байта больше.
4. Строки в Go тоже устроены весьма минималистично. Строка представляет собой структуру из двух значений размером в слово - в первом значении хранится длинна строки, а во втором слове - указатель на массив байт в котором фактически хранится строка. Примерно также строки хранятся практически во всех реализациях этого типа на C++.
5. Слайс представляет собой структуру из 3-х слов - указатель на массив в котором хранятся данные, длинну слайса и  вместительность (capacity) слайса. При создании слайса не происходит никакого копирования - просто появляется еще один указатель на тот же самый массив в котором хранятся данные самой строки, из которой создается слайс. 
6. Интерфейс - это на самом деле не то же самое что и структура, из которой был создан этот интерфейс. То есть на самом деле когда мы приводим экземпляр типа к какому-то интерфейсу получается совершенно новое значение - экземпляр интерфейса. Экземпляр интерфейса  представляет  собой структуру из двух указателей  - указатель на тот объект который фактически скрывается за интерфейсом и указательна таблицу, помогающую транслировать обращение к методам объявленным в интерфейсе к фактическим методам объявленным в объекте, от которого образован данный интерфейс. Это объясняет почему когда нужно передать в какую нибуть функцию объект произвольного типа, его объявляют просто как interface{} а не как указатель на интерфейс. Потому что сам интерфейс занимает не больше 2 машинных слов.

Если честно я восхищен насколько эффективно Go работает с памятью. Накладные расходы находятся на уровне С++ - и при всем этом Go полностью берет на себя управление памятью, избавляя программистов от знания всех тонкостей работы всяких "умных указателей",  которые при неграмотном применении плодят очень трудно уловимые баги. Это дает программисту возможность не замораиватся подсчетом ссылок а заниматся реализацией поставленной задачи.

PS
Для сравнения PHP для размешения 1 мегабайта данных в массиве требуется почти в 10 раз больше памяти. 

суббота, 19 января 2013 г.

Бага в PHP PDO Mysql

      Наткнулся  на замечательную багу в PHP PDO Mysql - после обновления PHP c 5.3.1 до 5.3.2  последний rowset в пакетном SQL запросе просто перестал возвращатся. В общем случае возможность выполнять несколько SQL запросов за одно соединение не часто используется - в основном в силу сложности аггрегирования несвязных запросов в один пакет. Но в моем случае mysql соединение использовалось для выполнения запросов к Sphinx. А в sphinxQL к примеру для того чтобы получить общее количество документов удовлетворяющих критерию - без учета выражения LIMIT необходимо отправлять сразу два запроса в одном пакете - непосредственно запрос по выборке документов и "SHOW META". Также если вы отправляете несколько схожих запросов одним пакетом - то сфинкс может их оптимизировать, кэшируя общие поддеревья. В общем пока придется дописывать в каждый пакет дополнительный запрос "SHOW META" чтобы как-то обходить это ограничение - другого решения этой проблемы я пока не знаю.

четверг, 17 января 2013 г.

понедельник, 14 января 2013 г.

NewRelic & APC

           NewRelic - одна из лучших систем мониторинга, если ее еще настроить под себя и интегрировать с процессом разработки (отправка нотификаций о деплоях  и тд) - то вообще конфетка.  Но сегодня наткнулся на один большой косяк - если включить на серверах APC с параметром apc.stat=0 то PHP агент NewRelic перестает. Вот уже 3 дня жду ответа от их тех поддержки - ни ответа ни привета. Можно было бы забить, но использование   apc.stat=0 дает ощутимый прирост производительности, поэтому не использовать это в продакшене - не самое лучшее решение.