Я очень люблю Template Toolkit и считаю его лучшим по набору возможностей, удобству и гибкости шаблонизатором ;)

Но есть одна проблема с этим шаблонизатором, а именно: за всё надо платить.
В данном случае за функциональность и гибкость приходится платить скоростью обработки.
Долгое время считал эту проблему нерешаемой, приходилось с грустью констатировать, что обработка шаблона стоит на первом месте по затратам времени при генерации большинства страниц...

Но хотелось невозможного...

Возникла идея кэшировать так называемые "подшаблоны", т. е. шаблоны, вызываемые из шаблонов более верхнего уровня.
То есть, например, у нас есть шаблон index.tt, в нём инклюдятся (с помощью INCLUDE или PROCESS) подшаблоны header.tt и footer.tt (общие шапка и подвал для всего сайта).
В header.tt и footer.tt инклюдится ещё что-то и т. п.
Так вот, <a href="http://www.reg.ru/">мы</a> заметили, что именно обработка подшаблонов в нашем случае занимает довольно много времени, до 90% и более от общего времени работы TT. При этом многие подшаблоны тянут данные из базы (с помощью моделей Catalyst) или ещё откуда-то, содержат кучу сложной логики. Всё это обрабатывается достаточно долго...
При этом, что самое обидное, выдача подшаблонов (те данные, что подставляются в вышестоящий шаблон) практически статична, мало меняется во времени.

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

Так вот...
Путь к невозможному начался с попыток профайлинга времени обработки подшаблонов.
В процессе исследования темы наткнулся на замечательную статью <a href="http://www.stonehenge.com/merlyn/LinuxMag/col75.html">Profiling in Template Toolkit via overriding</a>, без которой этот текст бы сейчас не писался.
Затем исследование <a href="https://svn.reg.ru/oss/Template-Context-Profileable/">продолжилось</a>.
И, наконец, закончилось написанием модуля <a href="https://svn.reg.ru/oss/Template-Context-SRS/">Template::Context::SRS</a>.
Хотел рассказать об этом модуле на YAPC 2010 и к тому времени "причесать" и выложить на CPAN, но чувствую не дотерплю и выкладываю сейчас. ;)
Перед выкладкой на CPAN модуль, конечно, нужно причесать: написать документацию и тесты и отвязать от нашего внутреннего модуля "SRS::Cache", но это очень легко делается, просто нужно этим заняться. Может быть кто-то даже поможет с этим ;)
Тем не менее, всё это работает. Работает стабильно. Уже несколько месяцев на production: "не единого разрыва" по причине механизма кэширования.

* * *

Инструкция по эксплуатации.
Всё очень просто:

1. Где-то в проекте тупо делается "use Template::Context::SRS;".

2. Затем в шаблоне, в котором хотим закэшировать выдачу какого-то подшаблона, делаем как-то так:

[% PROCESS index_reg_bar.inc
    lang = lang
    pricegroup = pricegroup
    __cache_time = -1
%]

Ключевым здесь является передача служебной переменной "__cache_time". Она и включает кэширование.
"-1" означает -- кэшировать вечно. Но можно задать конкретное время устаревания кэша в секундах.

Прочие параметры имеют не менее сакральный и важный смысл -- это не просто передача параметров в подшаблон,
а ещё и критерий для формирования ключа кэширования.
Ключи кэширования имеют значение в том случае, если выдача подшаблона зависит от каких-то условий: текущего языка и т.п.
Так вот, уникальная комбинация имён / значений передаваемых параметров и будет служить уникальным ключом кэширования.
Например, в предыдущем примере выдача шаблона зависит от двух параметров -- языка (lang) и тарифного плана пользователя (pricegroup).

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

[%- PROCESS
    header_screen_body_cached.inc
	new_messages_num			= user_messages.size() || user_messages_num || 0
	__nocache_banner_no			= banner_no
	__nocache_lang				= lang
	__nocache_wide_flag			= wide_flag
	__nocache_path_query			= path_query
	__nocache_user_id			= user_id
	__nocache_user_balance			= user_balance
	__nocache_servername			= servername
	__nocache_redirect_after_login		= redirect_after_login
	__nocache_path				= path
	__nocache_show_tooltips			= show_tooltips
	__nocache_show_tipsofday		= show_tipsofday
	__nocache_nostdheader			= nostdheader
	__nocache_tab_item			= tab_item
	__nocache_subtab_item			= subtab_item
	__nocache_load_token_js			= load_token_js
	__nocache_title_nopad			= title_nopad
	__nocache_page_title_body		= page_title_body
	__nocache_page_title			= page_title
	__nocache_req_method			= c.req.method
	__cache_time				= user_messages.size() ? 0 : -1
-%]

Параметры, начинающиеся с "__nocache_" -- фиктивные, используются только для формирования ключа кэширования.
В подшаблон они не попадают -- удаляются перед его вызовом.
В данном случае значения параметров, передаваемых через "__nocache_", всё равно попадают в шаблон -- как соответствующие глобальные переменные.
Например, "page_title" -- переменная, видимая в текущем неймспейсе и она же будет видна в неймспейсе подшаблона.
Можно было бы, конечно написать "page_title = page_title", но, по моему, это выглядит как-то странно.
С "__nocache_" вроде бы это более явно -- ясно, что этот параметр нужен только для формирования ключа кэширования и не используется в подшаблоне.

* * *

Итак, всё это работает. Результат достигнут -- обработка многих тяжёлых шаблонов, например, <a href="http://www.reg.ru/">главной страницы</a>, ускорилась в разы.
Этот текст занимает больше байт, чем сам модуль ;)
