Все значения, получаемые как свойства объектов, надо тупо и последовательно сохранять в переменные, чтобы не извлекать из объектов повторно. Ссылки на сами объекты хранить в реестре. А некоторые объекты превращать в более простые объекты (массивы)...

Кэширование промежуточных результатов в веб-программировании

Кэширование в php-скрипте

Существует такой шаблон проектирования (или приём программирования) «Экстрактор»: создаваемая конструкция не знает о существовании или отсутствии элементов, она просто пытается извлечь эти элементы с помощью неизвестно что делающих функций. Пример:

Если позже возникнет потребность ещё раз получить текст title, ничтоже сумняшеся используем ту же функцию:

В отличие от шаблона «Дефрагментация» (однократного получения и хранения текста title в специальном объекте или массиве), способ «Экстрактор» более удобен тем, что не надо помнить (знать) о том, получен ли уже текст нужного элемента, – способ «Экстрактор» предполагает получение текстов (элементов) «с нуля». Фреймворки часто используют именно такую стратегию получения данных. Именно потому, что она не требует обеспечения последовательности получения, позволяет не тратить на такую последовательность умственные ресурсы (принцип экономии усилий).

За это приходится платить производительностью. Ведь внутри функции get_title() наверняка будет обращение к БД, что-нибудь вроде:

И во втором, и в последующих случаях обращения к функции будет то же самое: обращение к БД, выполнение запроса. А это стрёмно для скорости. Шаблон проектирования «Дефрагментация» требует более сложной системы учёта извлекаемых из базы объектов, но зато обеспечивает больше кэширования. ТО есть он более кэширный, чем Экстрактор.

Кэширование – перекладывание (части) данных во временное хранилище для более быстрого доступа. При использовании шаблона проектирования «Дефрагментация» кэширование используется на полную катушку («жадное» кэширование): если уж скрипт залез в базу запросом, он постарается извлечь из запроса максимум за единственный проход и сохранить все результаты во временном хранилище – массиве ($global):

В этих упрощённых примерах разницы в скорости, скорее всего, не будет – потому что mysql-запросы такого рода обычно кэшируются самим сервером mysql (и второй раз сервер уже не будет заново обрабатывать запрос, а сразу отдаст php предыдущий готовый результат). Но на практике и сами запросы бывают сложнее, и php-кода «вокруг» запросов требуется намного больше. Да и не всегда данные извлекаются из базы mysql, часто для их получения требуются вычисления, которые нельзя кэшировать «на стороне сервера» (потому что сервера нет). Вот, например, как должно выглядеть получение реферера – страницы, с которой пользователь отправил текущий запрос? Такие переменные обычно получают с помощью примерно такой функции:

При использовании «Экстрактора» эта функция будет вызываться каждый раз, когда надо получить какое-то очередное значение переменной среды. При использовании «Дефрагментации» функция запускается один раз в начале скрипта, и все полученные переменные складываются в массив. Ясно, что второе сложнее, оно предполагает знание обо всех переменных окружения, которые могут быть использованы в течение жизни скрипта. То есть оно предполагает проектирование структуры скрипта, что, конечно, очень плохо и нудно для крутых и быстрых программеров.

Строго говоря, любые сведения о структуре данных БД, хранимые в виде кода в PHP-скрипте, можно считать кэшированием. Например, при генерации формы (или получении значений, отправленных из формы) очень желательно бывает знать об особенностях полей (является ли поле первичным ключом, числовое или текстовое, насколько оно велико...). Практически все необходимые данные такого рода всегда можно получить из самого mysql-запроса; но, вероятно, быстрее (и удобнее) записать все сведения о mysql-полях в массив в начале скрипта «раз и навсегда» (до очередного изменения структуры данных в БД!). Понятно, что такое решение требует перезаписи массива каждый раз при изменении структуры БД, и об этом приходится помнить человеку (что не очень технологично). Поэтому как поступать в этом конкретном случае, мы ещё не решили – будем пока использовать «смешанную стратегию» (создавать массив, но стараться по возможности получать данные «на ходу», из ресурса).

Кэширование в javascript

Если в php наиболее частым является кэширование данных, извлекаемых из Mysql, то для javascript данными являются объекты DOM на HTML-странице; и основным видом кэширования для javascript является именно сохранение информации об HTML-элементах. Современные javascript-фреймворки используют почти исключительно принцип «Экстрактор»: ничего не надо запоминать; никто ни про кого ничего не знает; неизвестно, какие задачи дальше будет решать скрипт, и кто вообще его будет использовать...

Например, при заполнении (и оформлении) html-формы нужно получить её через идентификатор:

Дальше происходит обработка элементов, ожидание действий пользователя. Потом пользователь заполняет поля, отправляет форму. При отправке нужно опять найти форму по ИД и проверить её элементы на заполненность; и опять форма находится тем же самым методом (1), хотя иногда его записывают через синонимы вида ge() или $(). Это стрёмно (неправильно). Очень правльно здесь использовать «жадное» кэширование: при первом доступе к любому элементу сразу заносить ссылку на него в глобальный «реестр»:

И потом не получать заново элемент методом getElementById, а обращаться к нему по ссылке window.reestr.editform. Один из приёмов такого кэширования – в начале работы скрипта создать ссылки в реестре для всех элементов, с которыми понадобится работать на протяжении жизни страницы.

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

О кэшировании самих объектов javascript внутри функций в сети написано немало, но несмотря на это, кэшированием упорно пренебрегают. Вот пример функции (кажется, это из prototype):

Во-первых, на..ра повторять в цикле конструкцию var element = ? var – действие, и оно требует каких-никаких ресурсов; правильнее вынести его «за скобки» – в начало скрипта:

Во-вторых, зачем два раза (а в принципе – сколько угодно раз больше единицы) получать свойство объекта arguments.lengh? Хорошая практика в этом случае – «закэшировать» полученное значение в переменной один раз в самом начале функции:

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

Почему это плохо? Вовсе не потому (как вы подумали), что мы не написали el.className += ' ' + c; – это нисколько не снижает нагрузку интерпретатора (а только упрощает написание). Мы три раза заставляем javascript лезть в «базу данных» DOM с запросом свойства el.className. Может, браузер (как и Mysql) сам кэширует результаты таких простых запросов, но лучше не надеяться на «бога из машины», а самому всё делать правильно, кэшэрно:

Все значения, получаемые как свойства объектов, надо тупо и последовательно сохранять в переменные, чтобы не извлекать из объектов повторно. Вот как в свете этого принципа будет выглядеть «классический» доступ к элементам формы (например, на стр. http://irweb.ir2.ru/add.php):

Читайте, завидуйте, думайте, экспериментируйте! ...пока jQuery не убил в вас последний проблеск живой мысли.

D.M., admin

Комментарии