Если первые два мифа относительно свежи, то второй представляет из
себя хороший пример типичной журналистской лжи. Это огрызок от фразы
«We should forget about small efficiencies, say about 97% of the time:
premature optimization is the root of all evil», притом фразы
сказанной
хорошим теоретиком но не особо каким крутым программистом Дональдом
Кнутом. Почему она тиражируется и по сей день, мне непонятно, причём
её повторяют потеряв основной смысл: не надо возиться с оптимизацией
по мелочам, надо бороться лишь с самыми серьезными проблемами.
Апогеем слияния этих мифов стал один проект, который мне недавно
поручили оптимизировать: до 40 секунд (!) на показ страницы.
Автор проекта вместо того, что бы немного подумать _вначале_, видимо
решил следовать принципу «оптимизировать будем, когда понадобится». По
крайней
мере именно так проще всего объяснить то, что я увидел, вскрыв
пациента. В итоге код был полон таких конструкций, когда
из базы вытаскиваются все статьи (немного обезличим предметную
область), к каждой потом тянутся рейтинги, потом статьи сортируются
внутри
рельс по рейтингу. Как сами можете догадаться у каждой статьи
перебирается вся коллекция рейтингов N log(N) раз, потому что даже
такая вещь как суммарный
рейтинг нигде не кешируется. Только после этого результирующая выборка
в памяти рельс подвергается постраничной нарезке.
Короче, результатом такого подхода было то, что на первую по
значимости страницу делалось 2000 (!) SQL запросов и 4000 AR объектов.
Несколько часов разбирательств в чужом коде
и количество упало до 40 запросов, причём не без структурных
изменений. Где-то проставлен :include, где-то рейтинг закеширован, а
где-то пришлось выбрасывать на помойку две ненужных промежуточных
модели.
То, чем я занимался, вовсе не называется оптимизацией в общепринятом
смысле. Это естественные меры по предупреждению очевидных проблем,
которые должен
был сделать автор кода ещё на этапе проектирования. Например, заложить
везде счётчики комментариев, рейтинги и прочую подобную лабуду прям в
базу.
В итоге заказчик обратился ко мне с очевидным для него вопросом: зачем
пишут на рельсах, если они так тормозят, хотя тормозили вовсе не
рельсы а способ их использования.
Кеширование в этой истории играло особую роль. Дело в том, что оно
было и работало. Его пришлось выдирать из проекта калёным железом,
потому что человек,
вставлявший код вида:
@article = Rails.cache.fetch(..) do
Article.find(params[:id])
end
вообще даже не пытался вникать в то, что же такое кеширование и почему
о нём все говорят. Для начинающих могу сразу сказать: скорее всего эта
штука вам не нужна вовсе.
В базе данных должны быть многие десятки и сотни миллионов записей в
одной таблице, что бы вытаскивание модели из кеша стало сравнимо по
скорости с прямым поиском по primary key в таблице.
Для среднего проекта в котором есть лишь пара таблиц, перемахнувших за
миллионный рубеж базу данных надо не кешировать, а профилировать.
EXPLAIN, slow query log,
замена mysql на postgresql или наоборот по необходимости. Разные
способы, но вся моя практика мне говорит, что гораздо проще вытянуть
из средних размеров базы записи, чем бороться с устарением кеша.
Причём хоть какое-то проектирование структуры БД — вещь нужная, это не
такая гибкая вещь как шаблоны.
Та же самая фигня с фрагментарным кешированием. Я своими глазами видел
кеширование куска HTML-я для конкретного пользователя:
Rails.cache.fetch("article-#{@article.id}-#{current_user.id}")
Не знаю, с какой целью человек добавлял по 50-100 мс на каждый показ
страницы и без того медленного сайта, но очевидные простые подсчёты
говорят нам о бессмысленности таких действий: глубина прокликивания
сайта не больше 8-10 страниц. Т.е. на каждый приход пользователя ему
добавляется несколько походов к мемкешу за данными, которые с 99%
вероятностью отсутствуют.
Понавтыканное хаотично кеширование — следствие непонимания процедуры
оптимизации. При оптимизации надо:
1) сначала осознать проблему. «У нас медленно показываются страницы
при посещаемости 10 тыс человек в день». Или «у нас медленно
показываются страницы при 10 млн в день». Это разные проблемы, которые
надо решать по-разному.
2) инструментами, отладочной печатью и прочими способами локализовать
основных потребителей ресурсов. Хороший пример, когда руби внезапно
стал работать быстрее при сборке с оптимизацией по размеру кода.
Скорее всего она начала влезать в L2-L1 и это дало буст в скорости.
Если отсортировать потребителей ресурсов по очевидности и простоте
локализации, то это:
* N+1 проблема с базой данных. Иногда решается простым :include, но
не всегда. Это надо понимать ещё перед тем, как заполнена и пропущена
миграция, потому что дальше будет сложнее менять. Разумное
планирование структуры БД — это не преждевременная оптимизация, это
разумное планирование.
* использование заведомо плохих инструментов, таких как RMagick или
ferret. Вычищение кода от феррета — это не оптимизация, это
исправление _ошибок_, допущенных на этапе проектирования.
* таки преждевременная оптимизация. Попытки закешировать всё, что
пролезет мемкешу в сокет приводит в двум проблемам: невалидные данные
(ой, а я удалила, а там не удалилось) и тормоза.
3) локализовав проблему надо понять, что стало её причиной. Например,
мне как-то раз хватило указания переменной TZ, что бы рельсы вздохнули
и отпустили CPU. Причина оказалась в функции strftime из glibc,
которая без необходимости лезет в файл /etc/localtime
4) по возможности устранить причину. Например, когда профилировал
морду у лукэтми, выяснилось что MySQL принципиально не может быстро
выбирать статьи для него. Это ограничение техническое и в ближайшие
несколько лет устранению не подлежит. Правильным решением было бы
заменить в той ситуации БД на постгрес, что бы получить быструю
выборку статей из БД. Есть и обратные примеры, когда самым простым
решением была замена на MyISAM.
5) и только при невозможности устранить причину проблемы уже стоит
задумываться о кешировании, масштабировании, очередях сообщений и
прочих модных словах, которыми полны западные блоги. Только когда
ясно, что проблему просто так не устранить. Я не знаю простой и
быстрый способа заставить рельсы рендерить быстро, так что, увы,
приходится куски HTML-я убирать в фрагментарный кеш, засовывать в
memcached и т.п.
Резюме такое: с базой данных работать проще, чем с кешом и зачастую
она быстрее, чем мемкеш (по крайней мере забандленный вариант,
написанный на руби). Что бы проект был проще, код чище и стабильнее
стоит разумнее подходить к вопросу проектирования сайта, лишь потом
заниматься устранением _существующих_, а не потенциальных проблем. А
кеширование зачастую — последний и не всегда нужный рубеж борьбы за
скорость сайта.
Вот, собственно, и ответ на ваш вопрос. Я подозреваю, что тот человек
который писал проект до вас и за которого вы теперь добавляете include
и счетчики в таблицы тоже обращал внимание только на техническую
сторону проекта и не думал ни о чем другом кроме нее. Кому нужно
спрашивать о каких-то скучных планах и спрашивать у заказчика планы по
числу пользователей через месяц, полгода, год? Ведь гораздо приятнее
забыть зачем создается приложение и залезть с головой в код и фигачить-
фигачить-фигачить. С другой стороны, заказчик тоже не торопится
раскрывать информацию о реальной нагрузке т.к. сторонние разработчики
часто даже не устроены в штат и не подписывают NDA и подобные
документы. Короче, встретились глухой ко всему кроме техники гик и
немой параноик. Начнется ли оживленная дискуссия? :)
В результате такого перекоса в техническую сторону гик получил данные
которые ему нужны сейчас для того чтобы писать код а не для того,
чтобы этот код грамотно работал через год. На момент написания кода
он, скорее всего, чудесно работал и все были довольны. Более того, из-
за описаной проблемы с общением, разработчик считал свой код хорошо
написанным. И он был прав исходя из тех данных о целях проекта которые
у него были на тот момент. Забавно, но возможно с его точки зрения
именно ваши счетчики и includ-ы были бы преждевременной оптимизацией.
А теперь самое интересное. А вы прежде чем браться за тюнинг
существующей архитектуры задавали вопросы о требованиях к проекту в
будущем? Или через год-два нам ждать подобную статью от человека
который будет тюнить уже ваш код?
On 23 фев, 16:42, Max Lapshin <max.laps...@gmail.com> wrote:
> В последнее время я часто наблюдаю материализацию некоторых мифов:
> 1) кеширование -- панацея от всех проблем, включая сомалийских пиратов
> Причём хоть какое-то проектирование структуры БД -- вещь нужная, это не
> такая гибкая вещь как шаблоны.
>
> Та же самая фигня с фрагментарным кешированием. Я своими глазами видел
> кеширование куска HTML-я для конкретного пользователя:
>
> Rails.cache.fetch("article...@article.id}-#{current_user.id}")
>
> Не знаю, с какой целью человек добавлял по 50-100 мс на каждый показ
> страницы и без того медленного сайта, но очевидные простые подсчёты
> говорят нам о бессмысленности таких действий: глубина прокликивания
> сайта не больше 8-10 страниц. Т.е. на каждый приход пользователя ему
> добавляется несколько походов к мемкешу за данными, которые с 99%
> вероятностью отсутствуют.
>
> Понавтыканное хаотично кеширование -- следствие непонимания процедуры
> оптимизации. При оптимизации надо:
> 1) сначала осознать проблему. <<У нас медленно показываются страницы
> при посещаемости 10 тыс человек в день>>. Или <<у нас медленно
> показываются страницы при 10 млн в день>>. Это разные проблемы, которые
> надо решать по-разному.
>
> 2) инструментами, отладочной печатью и прочими способами локализовать
> основных потребителей ресурсов. Хороший пример, когда руби внезапно
> стал работать быстрее при сборке с оптимизацией по размеру кода.
> Скорее всего она начала влезать в L2-L1 и это дало буст в скорости.
> Если отсортировать потребителей ресурсов по очевидности и простоте
> локализации, то это:
> * N+1 проблема с базой данных. Иногда решается простым :include, но
> не всегда. Это надо понимать ещё перед тем, как заполнена и пропущена
> миграция, потому что дальше будет сложнее менять. Разумное
> планирование структуры БД -- это не преждевременная оптимизация, это
> разумное планирование.
> * использование заведомо плохих инструментов, таких как RMagick или
> ferret. Вычищение кода от феррета -- это не оптимизация, это
> исправление _ошибок_, допущенных на этапе проектирования.
> * таки преждевременная оптимизация. Попытки закешировать всё, что
> пролезет мемкешу в сокет приводит в двум проблемам: невалидные данные
> (ой, а я удалила, а там не удалилось) и тормоза.
>
> 3) локализовав проблему надо понять, что стало её причиной. Например,
> мне как-то раз хватило указания переменной TZ, что бы рельсы вздохнули
> и отпустили CPU. Причина оказалась в функции strftime из glibc,
> которая без необходимости лезет в файл /etc/localtime
>
> 4) по возможности устранить причину. Например, ...
>
> продолжение >>
2010/2/23 Victor Brylew <victor...@gmail.com>:
> --
> --
> Данное сообщение отправлено Вам, так как Вы являетесь подписчиком группы "RubyOnRails to russian" на группах Google.
> FAQ группы находится по адресу: http://ru.wikibooks.org/wiki/RubyFAQ
>
> Для того, чтобы отправить сообщение в эту группу, пошлите его по адресу
> ror...@googlegroups.com
> Чтобы отменить подписку на эту группу, отправьте сообщение по адресу: ror2ru-un...@googlegroups.com
> Дополнительные варианты находятся на странице группы http://groups.google.com/group/ror2ru?hl=ru
--
Alexey Kovyrin
http://kovyrin.net/
Это только у меня такое ощущение, что Виктор набросился на Макса с
яростью, достойной только автора того самого кода, о котором идет
речь? :-)
Какие организационные вопросы? Какой "через год"? Если человек делал
find(:all), который потом нарезал на страницы в руби, а потом еще и
кешировал для current_user.id? Элементарный нуб подался "в
программисты" и наваял херню, которую потом еще и продал.
Кто виноват в том, что код поставлен ненадлежащего качества?
2010/2/23 Yaroslav Markin <yaro...@markin.net>Кто виноват в том, что код поставлен ненадлежащего качества?
"Программист на PHP Вася" виноват, однозначно. Потому как (а) косяки о которых идет речь - по большей части платформенно-независимы, и (б) профессиональная культура предполагает, что учиться либо под крылом у кого-то опытного, либо сначала на кошках (игрушечные проекты, open source и тп).
2010/2/23 Yaroslav Markin <yaro...@markin.net>:
--
--
Данное сообщение отправлено Вам, так как Вы являетесь подписчиком группы "RubyOnRails to russian" на группах Google.
FAQ группы находится по адресу: http://ru.wikibooks.org/wiki/RubyFAQ
Для того, чтобы отправить сообщение в эту группу, пошлите его по адресу
ror...@googlegroups.com
Чтобы отменить подписку на эту группу, отправьте сообщение по адресу: ror2ru-un...@googlegroups.com
Дополнительные варианты находятся на странице группы http://groups.google.com/group/ror2ru?hl=ru
И начальник не виноват никогда
Если в коде проекта, который вам передали — кошмар и ужас, то нужно не стебаться над глупым разработчиком, а задать менеджеру правильные вопросы
и вовремя умыть руки
Алексей, а ты вовсе не противоречишь Ярославу. Кто-то пишет плохо,
потому что мало опыта (это вообще дело понятное и житейское), кто-то
пишет плохой код, потому что обчитался блогов и неправильно применяет
инструменты про использование которых писали люди, понимающие в этом
ещё меньше него (давайте вспомним какой бред несли укурившиеся в хлам
thoughtbot-ы). А решения какого-то менеджера приводит к тому, что эта
работа принимается как есть без чьего бы то ни было фидбека и оценки,
так необходимых для личного роста программиста.
2010/2/23 Yaroslav Markin <yaro...@markin.net>И начальник не виноват никогда
Этого я вовсе не говорил. Ты сейчас рассуждаешь в модели "виноват кто-то один". Мне больше нравится модель "виноваты все, в разной степени". Как сказал т-щ Ковырин, автор кода виноват всегда. Заказчик - обычно тоже, но не обязательно, и как правило - в меньшей степени.
Стебаться над «глупым разработчиком» вообще последнее дело. Но и вовсе
не нужно расшаркиваться перед откровенной халтурой, мы тут не в
институте благородных девиц =)
Что же до менеджера и конкретного случая, то это всё таки немного
отходит от темы, которую я так пространно описал. Иначе говоря: какие
ошибки нельзя допускать на старте, а что является откровенной тонкой
оптимизацией, которую категорически нельзя проводить без данных
профилировки реального продакшна.
Ребята, не в Ruby/PHP дело, а в людях: "недо-", "полу-", "не-" программерах, низким уровнем профессиональной подготовки.
> Какие организационные вопросы? Какой "через год"? Если человек делал
> find(:all), который потом нарезал на страницы в руби, а потом еще и
> кешировал для current_user.id? Элементарный нуб подался "в
> программисты" и наваял херню, которую потом еще и продал.
Хорошо. Давайте на другом примере. Был проект который пришлось тюнить
мне. Проект был написан вполне грамотно: продуманы структуры, все
покрыто тестами, комментарии в нужных местах, код в целом грамотный
хотя и с некоторыми признаками церителизма. Вот только запустили этот
код в продакшене явно на данных в десятки, если не в сотни, раз больше
чем то, на что код расчитан. В чем же виноват разработчик? Варианты:
1) Не знал как написать правильно.
2) Не знал о том на каких объемах данных будет работать проект в
продакшене.
Оба варианта могли привести к тому коду который я видел, теоретически.
Но на мой взгляд, человек был вполне в состоянии сделать все то, что
делал я во время тюнинга. По коду было видно, что вполне мог написать
и сам. Так что мешало? На мой взгляд - причина номер 2. Почему эта
причина возникла - плохая связь между разработчиком и заказчиком,
нечеткие задания, ... Короче все те организационные вопросы которые вы
отметаете.
Разумно ли обращать внимание на первую возможную причину херового кода
и игнорировать вторую?
Угу, peace bros.
Я вот делаю свой соственный проектик.
И смешно человеку, как сказал классик, наблюдать как внутренний
заказчик кроет матом и тупого внутреннего нуба-программера. А тот не
остается в долгу.
М.