On my hosts doing several hundred requests per second the load was at
100 already, and then Adrian suggested to cache the templates. So I
implemented a simple cache (which is now a hack but perhaps it will be
included in Django soon) which brought the load down to 5 - so a lot of
speedup at the cost of templates not being reloaded when changed (you
have to restart apache or whatever you're using for Django).
The actual page load time on a non-loaded server went down from 0.17 to
0.07 seconds with the templates cached.
The code is below, all you have to do is call install_loader_cache()
when your app starts (in the urls perhaps) and use render_to_response
from this package when rendering output (the default one has an old
reference to get_template so it won't use the cache until this feature
gets included in Django).
Regards!
Wojtek
# django template cache by wsob...@gmail.com
from django.core import template_loader
from django.core.template import Origin, StringOrigin, Template,
Context
from django.utils.httpwrappers import HttpResponse
orig_get_template = template_loader.get_template
cache = {}
def get_template(name):
if cache.has_key(name):
return cache[name]
else:
tpl = orig_get_template(name)
cache[name] = tpl
return tpl
def render_to_string(template_name, dictionary=None,
context_instance=None):
"""
Loads the given template_name and renders it with the given
dictionary as
context. The template_name may be a string to load a single
template using
get_template, or it may be a tuple to use select_template to find
one of
the templates in the list. Returns a string.
"""
dictionary = dictionary or {}
if isinstance(template_name, (list, tuple)):
t = template_loader.select_template(template_name)
else:
t = get_template(template_name)
if context_instance:
context_instance.update(dictionary)
else:
context_instance = Context(dictionary)
return t.render(context_instance)
def render_to_response(*args, **kwargs):
return HttpResponse(render_to_string(*args, **kwargs))
def install_loader_cache():
global orig_get_template
template_loader.get_template = get_template
template_loader.render_to_string = render_to_string
You might have written this as a simple template loader - and used the
basic template loaders as starting point, just overloading the
get_template functionality. That way you would be able to use the
standard render_to_xxx functions - you only would have to change the
TEMPLATE_LOADERS setting. And that way integration with the Django core
is quite simple, as it is just another bunch of template loaders the
user can just activate.
One more thing: maybe you need to protect your "cache" global with
locks, because otherwise this might produce funny results in
multithreaded situations. Or use the Django cache API, so people can
switch between different caching backends like memcached (which would
make the cached templates available to all processes at once) or local
memory (which would be similar to your code).
Yes, I understand that you needed a fast hack for your problem and
aren't maybe too interested into hacking more on this, so see this more
as possible "implementation notes" for people who take up from this
idea ;-)
bye, Georg
I'm not sure that would work, because I suspect much of the
performance savings Wojtek is getting is from the fact that his patch
caches *compiled* templates, not the raw template code. (He has a
template that loads another template within a loop.) I don't see how a
template loader -- in the TEMPLATE_LOADERS framework -- would be able
to cache compiled templates.
I was thinking we could add a CACHE_COMPILED_TEMPLATES setting and
change get_template() to give it a "cache" keyword argument, which
would default to the value of CACHE_COMPILED_TEMPLATES. Any quick
thoughts before I implement this?
Adrian
--
Adrian Holovaty
holovaty.com | djangoproject.com | chicagocrime.org
Or a way for the template loader to tell the machinery that the
template is already compiled? That way a template loader could cache
compiled templates and on access return the compiled template to
Django, instead of the template source?
I would prefer it much if this template caching could be moved to the
template loader completely, because it's a much saner approach than
adding yet another setting - every setting makes the template system
more dependend on the settings and therefore makes more complicated to
separate it (to allow people to use the Django template system without
the rest of the machinery).
bye, Georg
I think being able to edit/change templates without having to restart
the Web server (in Apache's case) is a feature. :) And Fredrick came
up with a really nice way of decoupling template settings in another
thread.