- Includes: useful to incorporate some snippets of content that in most cases are common to the site, e.g. footer, scripts, styles, etc.
- Extends: useful to define a master layout for the majority of the site content with placeholders, e.g. sidebar, horizontal menu, content, etc. The content developers extend the master layout by substituting available placeholders.
- Widgets: usually small snippets of highly reusable markup, e.g. list item, button, etc. The content developers use widgets to increase readability and enforce consistency of design.
Here are raw numbers:
05-template len(items) == 0 01-initial msec rps tcalls funcs django 13666 7317 146 40 jinja2 3464 28867 26 20 mako 7811 12802 47 35 tenjin 2715 36831 27 21 tornado 2318 43135 28 17 wheezy.template 573 174441 14 8 02-include msec rps tcalls funcs django 25101 3984 300 45 jinja2 14468 6912 98 36 mako 32303 3096 149 49 tenjin 9543 10479 88 27 tornado 2473 40441 35 17 wheezy.template 1239 80713 29 13 03-extends msec rps tcalls funcs django 45856 2181 517 68 jinja2 17431 5737 149 42 mako 45842 2181 226 64 tenjin 13998 7144 133 38 tornado 2608 38350 40 17 wheezy.template 2249 44469 47 18 04-preprocess msec rps tcalls funcs django not available jinja2 not available mako not available tenjin not available tornado not available wheezy.template 572 174888 14 8 len(items) == 10 01-initial msec rps tcalls funcs django 133621 748 1352 49 jinja2 12825 7797 117 20 mako 14348 6970 136 35 tenjin 6718 14886 46 21 tornado 9644 10369 179 17 wheezy.template 2535 39449 73 8 02-include msec rps tcalls funcs django 144662 691 1506 53 jinja2 24081 4153 189 36 mako 38297 2611 238 49 tenjin 13657 7322 107 27 tornado 9801 10203 186 17 wheezy.template 3291 30382 88 13 03-extends msec rps tcalls funcs django 169264 591 1723 72 jinja2 27726 3607 262 42 mako 52785 1894 315 64 tenjin 18122 5518 137 35 tornado 9825 10179 191 17 wheezy.template 4421 22622 106 18 04-preprocess msec rps tcalls funcs django not available jinja2 not available mako not available tenjin not available tornado not available wheezy.template 2484 40263 73 8 06-widgets len(names) == 0 01-initial msec rps tcalls funcs django 5093 19633 63 30 jinja2 2181 45841 16 14 mako 6211 16101 35 32 tenjin 2087 47922 24 20 tornado 1787 55969 16 14 wheezy.template 332 300797 8 7 02-single msec rps tcalls funcs django 8925 11204 104 31 jinja2 5890 16977 38 33 mako 14460 6916 74 50 tenjin 4414 22655 45 24 tornado 5095 19629 45 27 wheezy.template 621 160939 14 9 03-loop msec rps tcalls funcs django 5244 19071 63 30 jinja2 4534 22054 30 28 mako 13288 7526 65 49 tenjin 2110 47403 24 20 tornado 1615 61929 16 14 wheezy.template 408 244825 9 8 len(names) == 1 01-initial msec rps tcalls funcs django 12625 7920 127 45 jinja2 3243 30836 21 18 mako 6900 14493 42 35 tenjin 2645 37802 26 21 tornado 2436 41052 28 17 wheezy.template 460 217192 12 8 02-single msec rps tcalls funcs django 16400 6098 168 46 jinja2 7001 14284 43 38 mako 15181 6587 81 52 tenjin 4948 20208 47 25 tornado 6034 16572 57 30 wheezy.template 757 132115 18 10 03-loop msec rps tcalls funcs django 16266 6148 168 46 jinja2 7063 14157 43 37 mako 15183 6586 81 52 tenjin 4859 20581 43 25 tornado 6060 16501 57 30 wheezy.template 766 130472 18 10 len(names) == 10 01-initial msec rps tcalls funcs django 62323 1605 685 45 jinja2 8906 11229 66 18 mako 11950 8368 105 35 tenjin 5126 19507 44 21 tornado 7809 12806 136 17 wheezy.template 1498 66765 48 8 02-single msec rps tcalls funcs django 67078 1491 726 46 jinja2 12128 8246 88 38 mako 20386 4905 144 52 tenjin 7380 13550 65 25 tornado 11465 8723 165 30 wheezy.template 1937 51633 54 10 03-loop msec rps tcalls funcs django 106136 942 1095 46 jinja2 21863 4574 160 37 mako 26057 3838 225 52 tenjin 26474 3777 214 25 tornado 45056 2219 426 30 wheezy.template 3676 27203 99 10msec - a total time taken in milliseconds, rps - requests processed per second, tcalls - total number of calls made by corresponding template engine, funcs - a number of unique functions used.
Setup and Run
Prerequisites to be able run this in a clean debian testing installation.
apt-get install make python-dev python-virtualenv \
unzip
The source code is hosted on bitbucket, clone it into some directory and setup virtual environment (this will download all necessary package dependencies per framework listed above).
hg clone https://bitbucket.org/akorn/helloworld cd helloworld make env -sC 05-template make env -sC 06-widgetsNote, you can run this benchmark using any version of python, including pypy. Here are make targets to use:
make env VERSION=3.3 make pypyOnce environment is ready, cd to benchmark directory and run:
env/bin/python benchmark.pyEnvironment Specification:
- Intel Xeon CPU X3430 @ 2.40GHz x 4, Kernel 3.2.0-4-amd64
- Debian Testing, Python 2.7.3



I followed your series of web components benchmarks but I'm not sure how relevant they are to the "real world". If you take the slowest template engine in the slowest test it is 596 RPS = about 2ms per request. And for a fast website using SQL it takes let's say 50ms (probably more) to render complete HTML response, with majority of time spent waiting for SQL server and logging to files.
ReplyDeleteSo even the slowest template engine takes only 4% of time, and you're optimizing this 4% part. I'm not saying it's useless to optimize these (for sure there are use cases and loads when it matters), only that for most people functionality is what matters much more.
Why not put cache between web application and database? That changes use case dramatically. Don't you think?
DeleteI think when cached values are kilobytes in size then even memcached give >1ms latencies. Also a single uncached HDD access means at least 8-10ms latency (not necesserily SQL access, can be eg. logging). I would say that 50ms for computing server response for a real world website is very fast.
DeleteI would suggest take a look at C10K topic. The question is about efficiency.
DeleteThank you for the nice benchmark, especially for handing out the source. It would be interesting to see how django templates perform if you activate the cached template loader (I assume it will be considerably faster):
ReplyDeleteTEMPLATE_LOADERS = (
('django.template.loaders.cached.Loader', (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)),
)
I believe this is how it used, actually. See the source:
Deletehttps://bitbucket.org/akorn/helloworld/src/tip/05-template/django/app.py
I always get annoyed by these "how relevant are your benchmarks on an application?" / "Use a cache". Well, template rendering IS a part of an application, and it's easier and more wise to benchmark isolated parts then the whole. Everyone knows what caching can do for a web application and that a web application is not just templating. He's not claiming application for application performance, but TEMPLATING. SO, thanks for the test, it is very relevant.
ReplyDeleteBy the way, not all applications using templates are web ones, and not every template is cacheable.
Nice article! It would be interesting if Tenjin were included in it.
ReplyDeleteI have updated post with tenjin in the list now.
Delete