С недавним обновлением RLib был оптимизирован поиск служебных классов "rload_controllers" во всех элементах страницы.
Приведу немножко статистики и результаты профилирования.
Чуть раньше поиск спец. классов "rload_controllers" выполнялось через проход по словарю известных rload_классов и поиском совпадений в DOM-е через jQuery('.rload_somecontroller') для каждого элемента словаря. Десятки раз вызывалась функция-обертка jQuery() и десятки тысяч jQuery.hasClass().
По результатам профилирования в Firebug на главной странице боевой rutube.ru, среднее число вызовов всех функций на сайте составляло 120-130 тыс. и среднее время выполнения скриптов 1.1-1.2 секунды.
После рефакторинга RLib в части метода dynamicLoad (в котором производится основная часть проходов по DOM), цикл проходов по словарю был заменен одни проходом по DOM посредством метода document.getElementsByTagName('*') и сравнением классов каждого элемента с элементами rload-словаря.
По результатам профилирования в бою, число вызовов функций составило 60-70 тыс.и время выполнения 470-550 мс.
Таким образом:
- суммарное число вызовов функции hasClass() сейчас 20-40 тыс вызовов, время работы 30-60мс;
- суммарное время функции dynamicLoad = 70-80мс.
У меня появилась идейка как можно еще больше оптимизировать это достаточно узкое место.
Например, если вынести спец. метки контроллеров в верстке (rload_controllers) из атрибута class в специальный атрибут например data-rload, тогда можно будет в одном проходе по всем элементам сразу ловить метки для подгрузки контроллеров, и избавиться от вызовов достаточно медленной функции hasClass в этом критичном месте.
Так как doctype документа XHTML, это не противоречит стандарту.
Плюсы:
1) избавляемся от вызовов hasClass и выигрывает 20-40 тысяч вызовов, и соответственно время загрузки.
2) перестаем быть сильно чувствительны к разрастанию DOM, т.к. сейчас чем больше элементов на странице, тем больше вызовов достаточно медленной hasClass()
Я подготовил тест производительности, который подтверждает что реализация с getAttribute() почти в 20 раз быстрее hasClass() который реализован у нас сейчас: http://jsperf.com/getattrvshasclass
Чем грозит:
1) переделка всех шаблонов, где подключение контроллеров выполняется в атрибутах class="" и страшный мердж в svn :)
2) соглашение в дальнейшем подключать js-контроллеры через атрибут data-rload
Скорее всего выигрыш составит не больше 80-100 мс, но так по крупицам сможем суммарно увеличить время загрузки.
Коллеги, интересно ваше мнение по этому поводу.
вторник, 30 августа 2011 г.
вторник, 23 августа 2011 г.
понедельник, 22 августа 2011 г.
среда, 17 августа 2011 г.
пятница, 12 августа 2011 г.
Интерфейс балансера
- В транке появился клиент балансера - Rutube::VideoBalancer2. Из полезного функционала на текущий момент позволяет выполнять RPC-вызовы API балансера.
- После короткого обсжудения, пакет RPC::XML принят дефакто стандартом для использования в перловом коде. С его помощью можно создавать как клиентов, так и серверы XML-RPC.
среда, 10 августа 2011 г.
Думай, прежде чем начнёшь программировать!
Очень кстати под руку подвернулась старая статья
Before coding… Think! | Making Good Software
Если кратко, то она раскрывает смысл известной пословицы "Одна голова хорошо, а две лучше!". Но советую прочитать целиком, так как вся соль в деталях.
Всем хорошего дня.
понедельник, 8 августа 2011 г.
Бессмертная статья Кента Бека
Сегодня снова увидел а своём ридере и с удовольствием перечитал. Великолепный конспект!
Ярлыки:
design
Версионная миграция cron-таблиц и БД
Коллеги, я накидал пару описаний
Версионная миграция cron-таблиц
Версионная миграция БД
и создал соотвествующие структуры в SVN в транке.
Это очень сырой вариант процедур, но мы их начнём обкатывать на DRM и ПРД. Очень надеюсь, что все примут активное участие в их улучшении.
Лёша Л. тебе нужно это внедрить в свой процесс релиз менеджмента и возможно автоматизировать рутинные операции.
Прошу конкретные замечания и предложения кидать в конфлюенс. А флудить лучше в блог.
Версионная миграция cron-таблиц
Версионная миграция БД
и создал соотвествующие структуры в SVN в транке.
Это очень сырой вариант процедур, но мы их начнём обкатывать на DRM и ПРД. Очень надеюсь, что все примут активное участие в их улучшении.
Лёша Л. тебе нужно это внедрить в свой процесс релиз менеджмента и возможно автоматизировать рутинные операции.
Прошу конкретные замечания и предложения кидать в конфлюенс. А флудить лучше в блог.
четверг, 4 августа 2011 г.
Cтранное поведение Perl
perl -e "sub a { use strict; some_shit {print 'ass'; return 1;}; }; print a();"
Что вернёт код?
Что вернёт код?
среда, 3 августа 2011 г.
Баг с memcached
Всем хеллоу!
Во время тестирования trackinfo обнаружили баг связанный с тем что обрывается соединение с сервером. Причина, предположительно, модуль Cache::Memcached::Fast считает что во время первого соединения произошла ошибка и лочит сервер на failure_timeout секунд. Лочить или нет устанавливает свойство max_failures. Эта ошибка воспроизводится только во время работы в окружении fastcgi. Сейчас из конфига мемкешей убрали опцию
max_failures (если это значение не указано либа ставит его в 0).
К сожалению у этой библиотеки нет debug режима =(. Ещё одно замечание, если поменять базовый класс в Rutube::Memcached c Cache::Memcached::Fast на Cache::Memcached то всё отлично! WTF? Oo
Отсюда делаем предположительный вывод, что либа засекает ошибку при первом обращении по ключу (оно кстати возвращается), затем вырубает сервак и после этого не может найти ключ в остальном пуле серваков.
Воспроизвести подобное поведение в скрипте не удалось =(
use Perl or die(); гы-гы =)
Во время тестирования trackinfo обнаружили баг связанный с тем что обрывается соединение с сервером. Причина, предположительно, модуль Cache::Memcached::Fast считает что во время первого соединения произошла ошибка и лочит сервер на failure_timeout секунд. Лочить или нет устанавливает свойство max_failures. Эта ошибка воспроизводится только во время работы в окружении fastcgi. Сейчас из конфига мемкешей убрали опцию
max_failures (если это значение не указано либа ставит его в 0).
К сожалению у этой библиотеки нет debug режима =(. Ещё одно замечание, если поменять базовый класс в Rutube::Memcached c Cache::Memcached::Fast на Cache::Memcached то всё отлично! WTF? Oo
Отсюда делаем предположительный вывод, что либа засекает ошибку при первом обращении по ключу (оно кстати возвращается), затем вырубает сервак и после этого не может найти ключ в остальном пуле серваков.
Воспроизвести подобное поведение в скрипте не удалось =(
use Perl or die(); гы-гы =)
вторник, 2 августа 2011 г.
Атомарная операция test-and-set на мемкеше
Из доки на мемкеш-клиент:
# Do atomic test-and-set operations. my $cas_val = $memd->gets('nkey'); $$cas_val[1] = 0 if $$cas_val[1] == 12; if ($memd->cas('nkey', @$cas_val)) { print "OK, value updated\n"; } else { print "Update failed, probably another client" . " has updated the value\n"; }
Баг ОРМ'а
Вкратце, баг касается переиспользования объекта, созданного с использованием параметра obj_new =1. Актуально при маппинге на таблицы с первичным ключом без auto_inctement. В этом случае после использования $obj->save(), значение ключа "убивается" и $obj->getID() выдает значение 0. Соответственно, при следующей попытке сохранения, в базе создается новая запись, соответствующая данному значению ключа (id=0), вместо ожидаемого апдейта существующей.
Ниже приведен пример, позволяющий убедиться в данной неприятности.
1) Таблица в БД:
CREATE TABLE `test` (
`id` int(11) NOT NULL,
`some_value` varchar(255),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=koi8r;
2) ОРМ:
package Rutube::Obj::Test;
use warnings;
use strict;
use Rutube::Object;
use base qw[Rutube::Object];
mapper( '-table' => 'test', '-key' => 'id' );
set_property_type('db');
property('id');
property('some_value');
1;
__END__
3) Тестовый скрипт:
#!/usr/bin/perl -w
use strict;
use warnings;
use lib qw|../lib|;
use Rutube::Config;
use Rutube::ObjectRegistry;
use Rutube::Obj::Track;
my $conf = Rutube::Config->new('../conf/.config');
Rutube::ObjectRegistry->setNamespace('offline');
use Rutube::Obj::Test;
my $obj = Rutube::Obj::Test->new({id => 123, some_value => 'asdf'}, {obj_new => 1});
printf "[id=%3d|some_value=%s]\n", $obj->getID(), $obj->getSomeValue();
# [id=123|some_value=asdf]
$obj->save();
printf "[id=%3d|some_value=%s]\n", $obj->getID(), $obj->getSomeValue();
# [id= 0|some_value=asdf]
$obj->setSomeValue("qwerty");
$obj->save();
printf "[id=%3d|some_value=%s]\n", $obj->getID(), $obj->getSomeValue();
# [id= 0|some_value=qwerty]
exit 1;
4) Имеем в результате:
Ниже приведен пример, позволяющий убедиться в данной неприятности.
1) Таблица в БД:
CREATE TABLE `test` (
`id` int(11) NOT NULL,
`some_value` varchar(255),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=koi8r;
2) ОРМ:
package Rutube::Obj::Test;
use warnings;
use strict;
use Rutube::Object;
use base qw[Rutube::Object];
mapper( '-table' => 'test', '-key' => 'id' );
set_property_type('db');
property('id');
property('some_value');
1;
__END__
3) Тестовый скрипт:
#!/usr/bin/perl -w
use strict;
use warnings;
use lib qw|../lib|;
use Rutube::Config;
use Rutube::ObjectRegistry;
use Rutube::Obj::Track;
my $conf = Rutube::Config->new('../conf/.config');
Rutube::ObjectRegistry->setNamespace('offline');
use Rutube::Obj::Test;
my $obj = Rutube::Obj::Test->new({id => 123, some_value => 'asdf'}, {obj_new => 1});
printf "[id=%3d|some_value=%s]\n", $obj->getID(), $obj->getSomeValue();
# [id=123|some_value=asdf]
$obj->save();
printf "[id=%3d|some_value=%s]\n", $obj->getID(), $obj->getSomeValue();
# [id= 0|some_value=asdf]
$obj->setSomeValue("qwerty");
$obj->save();
printf "[id=%3d|some_value=%s]\n", $obj->getID(), $obj->getSomeValue();
# [id= 0|some_value=qwerty]
exit 1;
4) Имеем в результате:
| id | some_value |
|---|---|
| 0 | qwerty |
| 123 | asdf |
Подписаться на:
Сообщения (Atom)