Представим, что у нас есть система, в которой каждый модуль собирает кусок XML-дерева. Потом результат каким-то образом проворачивается через XSLT-процессор и отдается юзеру. У нас есть желание закэшировать результат работы модуля средствами файловой системы. Собственно, я придумал 3 способа как это сделать и набросал небольшой тестик. Подробности под катом.
Собственно, вот тут можно пощупать работающий тест и поглядеть исходники. Сильно не мучать :) Для теста мы написали модуль testModule, который создает XML-поддерево, содержащее меню. Для простоты данные, из которых формируется меню, мы записали в private-member уже в структурированном виде, но вообще надо представить, как будто они берутся из БД и это типа долго :) Для каждого из способов кэширования я написал cache-класс, который подставляется в модуль. Метод run() модуля сначала пытается получить уже собранный узел от Cache'а, если не получает - формирует узел заново (ну, т.е. у нас просто перегоняет готовый массив в DOM, а в реальности он должен сначала вытаскивать его из базы данных), при этом кэш-класс записывает то, что он должен кэшировать. Действия ("action") модуля выполняются двумя методами: первый собирает структуру (должен называться action_getStructure), второй перегоняет ее в XML (называется action_structure2XML), где "action" - название действия. Если не использовать кэширование структуры, их можно объединить. В нашем примере тестируется единственный action "show_menu", соответственно используются методы show_menu_getStructure() и show_menu_structure2xML(); Теперь о способах кэширования. В классе Cache мы определяем всякие общие операции, типа передачи модуля и действия, с которыми работает кэш. От этого класса наследуются наши тестируемые пациенты: 1. Кэширование структуры, class structureCache Тут мы кэшируем не XML, а собранную структуру объектов/массивов. Соответственно, структура каждый раз вытаскивается из файла, unserialize'ится и перегоняется в DOM вызовом метода action_structure2XML() из кэш-объекта. У этого способа есть одно преимущество: он годится, когда XML должен меняться в зависимости от каких-то параметров. Например, если нужно текущему пункту меню выставить атрибут "is_current" - это делается сразу же при построении поддерева, а в остальных случаях придется уже собранное поддерево модифицировать. 2. Кэширование документа, class docCache В этом случае при кэшировании создается новый пустой DOMDocument. В него импортируется узел, созданный модулем, и сохраняется целиком документ (при этом его documentElement - это как раз корень поддерева модуля). Соответственно, при извлечении данных из кэша XML-данные загружаются в пустой документ, и его корневой элемент импортируется в основной документ. 3. Кэширование фрагмента, class fragmentCache Этот класс работает примерно как предыдущий, но вместо экспорта-импорта узла используется documentFragment. При сохранении кэша вызывается метод saveXML($node) основного документа, где $node - это элемент, сформированный модулем - таким образом можно сохранить в XML-строку отдельный DOMElement (спасибо CatManZero за совет на phpclub.ru - я раньше об этой фиче не знал). При загрузке кэша в рабочем документе создается documentFragment, при помощи метода appendXML в него помещается наше сохраненное поддерево, и кэш-класс возвращает этот фрагмент в модуль. По-хорошему, тут надо бы придумать, как возвращать DOMElement, ну да не так это важно. 4. Прогон без кэширования Для сравнения я добавил один прогон без использования кэша. Для "натуралистичности" в метод action_getStructure() добавлен рассчет 5000! - это чуть меньше 0.002 сек., что примерно соответствует времени выполнения не очень тяжелого SQL-запроса :)
Как организованы тесты можно посмотреть в исходниках. Собственно, выполнение модуля прогоняется по 1000 раз с кэш-объектами разных классов, и еще раз вообще без кэша. Класс Test сохраняет результаты, а затем отрисовывает график при помощи Google Chart API - если кому интересно, могу как-нибудь рассказать подробнее об этой штуковине. Самым быстрым оказался способ с загрузкой через documentFragment. Он же, кажется, самый правильный. Время загрузки при сериализации структуры будет увеличиваться при усложнении дерева быстрее, чем в при других способах. Время работы без кэша сильно зависит от сложности операций при сборке структуры. Если вдруг кто-нибудь повторит это безобразие, используя memcached вместо файловой системы - будет круто :)
memcached как минимум.
Мне кажется тест не показательный: надо исключать по возможности процессинг большого кода и оставлять примитивы и их сравнивать на разных серверах и разных запросах.