Thursday, July 19, 2012

Python Fastest Template Engine

What is the fastest template system for Python? See also:
  • Comprehensive benchmarks for various template include, extends and widget directives are here.
The benchmark is based on wheezy.template big table test for rendering a 10x1000 HTML table in unicode. Latest available versions (April 2, 2015):
  1. bottle 0.12.8
  2. chameleon 2.22
  3. cheetah 2.4.4
  4. django 1.8
  5. jinja2 2.7.3
  6. mako 1.0.1
  7. durusworks qpy 1.2
  8. spitfire 0.7.15
  9. tenjin 1.1.1
  10. tornado 4.1
  11. web2py 2.0.9
  12. wheezy.template 0.1.159
Let setup virtualenv environment:
virtualenv env
env/bin/easy_install -O2 bottle chameleon cheetah django \
  jinja2 mako tenjin webext tornado wheezy.html \
  wheezy.template
Download bigtable.py test:
wget https://bitbucket.org/akorn/wheezy.template/raw/tip/demos/bigtable/bigtable.py
And run it:
env/bin/python bigtable.py
Raw numbers (Intel Core 2 Duo @ 2.4 GHz x 2; OS X 10.10):
cpython 2.7.9
                     msec    rps  tcalls  funcs
bottle              94.06  10.63  163016     23
chameleon           70.96  14.09  161031     24
cheetah            179.02   5.59  225019     22
django            1135.57   0.88 1490076     65
jinja2              57.03  17.54   60018     26
list_append         11.70  85.44   63007      9
list_extend         10.36  96.49   23007      9
mako                45.16  22.14   93035     36
qpy_list_append    282.92   3.53  505023     14
spitfire            43.33  23.08  124016     23
tenjin              13.38  74.73   43010     13
tornado             80.96  12.35  233021     23
web2py             106.45   9.39  295015     20
wheezy_template     11.72  85.34   63009     11

pypy 2.5.1
                     msec    rps  tcalls  funcs
bottle              31.56  31.68  163020     25
chameleon           38.50  25.98  193036     25
cheetah             69.88  14.31  755039     33
django              79.72  12.54 1483081     65
jinja2              31.36  31.89  123018     27
list_append         20.17  49.57  103007     10
list_extend         25.21  39.67   63007     10
mako                22.02  45.42  153037     39
qpy_list_append            not installed
spitfire            65.87  15.18  123015     21
tenjin             252.41   3.96   13010     11
tornado             31.02  32.24  233021     23
web2py              30.16  33.15  295015     20
wheezy_template     20.62  48.50  103009     12

cpython 3.3.6
                     msec    rps  tcalls  funcs
bottle              62.30  16.05  133017     21
chameleon           87.64  11.41  182033     25
cheetah                    not installed
django            1115.48   0.90 1440079     62
jinja2              65.61  15.24   60020     27
list_append         15.11  66.16   63008     10
list_extend         12.61  79.29   23008     10
mako                51.16  19.55   93036     37
qpy_list_append    257.25   3.89  495025     16
spitfire                   not installed
tenjin              50.11  19.96  123012     16
tornado            183.37   5.45  353023     23
web2py                     not installed
wheezy_template     14.03  71.28   63010     12

pypy3 2.4.0
                     msec    rps  tcalls  funcs
bottle              22.06  45.32  133021     23
chameleon           28.11  35.58  214038     26
cheetah                    not installed
django             138.26   7.23 1463082     64
jinja2              78.77  12.70  123020     28
list_append         15.05  66.43  103008     11
list_extend         17.88  55.93   63008     11
mako                19.68  50.81  153038     40
qpy_list_append            not installed
spitfire                   not installed
tenjin              21.44  46.64  123012     16
tornado             43.44  23.02  353025     25
web2py                     not installed
wheezy_template     15.64  63.94  103010     13
tcalls - a total number of calls, funcs - a number of unique functions used.

Looking for other benchmarks? Take a look at python web frameworks benchmarks for routing, url reverse and web frameworks.

22 comments :

  1. Great.

    How about str() or html escape?
    I had found that the bottle neck of template engine is in str() or html escape, not in list.append or ''.join().
    (See http://www.slideshare.net/kwatch/how-to-create-a-highspeed-template-engine-in-python for details.)

    If you have time, could you try webext?
    http://www.kuwata-lab.com/tenjin/pytenjin-users-guide.html#tips-webext
    Or enforce tenjin.Engine not to call str() function?

    engine = tenjin.Engine(tostrfunc='')

    # I'm the author of Tenjin and above presentation slide.

    By the way, Jinja2, Mako, Tenjin, and wheezy.template have enough performance for web application. I believe that the performance bottle-neck of web application is not in them.
    But... it is fun to pursue high performance of template engine!

    ReplyDelete
    Replies
    1. Thank you for your comments. I have added webext optimization for tenjin. Please notice, there is huge performance degradation once I have webext installed for pypy (the benchmark for pypy does not have it installed).

      Delete
    2. Note, webext can not be installed into python3.3 virtual environment. Here are few errors:

      webext.c:89:18: error: ‘PyString_Type’ undeclared (first use in this function)
      webext.c:97:23: error: ‘PyInt_Type’ undeclared (first use in this function)

      Delete
    3. If you have a forum or blog, the number of comments/replies definitely load faster or slower with different template engines.

      Delete
  2. It would be interesting to see Cheetah on this list: http://cheetahtemplate.org/

    ReplyDelete
  3. For some reason when I run your bigtable.py benchmark to compare the lot against QPY (https://www.mems-exchange.org/software/), wheezy_template is performing roughly on par with list_append/list_extend. Any ideas why? This on a Linux Mint Debian Edition VM, Python 3.3.2.

    QPY btw is turning in 60 rps vs 29 rps for list_*. I'll put examples up on my site shortly.

    ReplyDelete
    Replies
    1. The wheezy.template translates markup to the code identical to list_append example.

      The difference in results is related to CPU switching for given process, thus you need stick it with one processor (use taskset) and make higher number of iterations (eliminate django first).

      Delete
  4. I've put up a post with some code samples and the results obtained for all the packages I'd installed in a Python 3.3.2 virtual instance:

    http://mikewatkins.ca/2013/06/12/python-fastest-template-engine/

    QPY is fast but isn't a traditional HTML or text templating system as most know them these days. Thankfully when a different itch needs scratching there's a nice selection of web tools for Python developers that are "fast enough".

    ReplyDelete
    Replies
    1. The top of the post has a reference to in-depth benchmarks for template engine directives like include, extends and widgets.

      Delete
    2. The python unicode string concatenation via join is inefficient due to continuous buffer re-locations. While wheezy.template remains pure python implementation it rely on python core efficiency.

      While evaluating impact of such improvement to real world application the result was so minimal that I decided to keep thing as is.

      Good to know QPY went even further by utilizing C optimizations.

      Delete
  5. Another template system that'd be good to have up there is Spitfire.
    https://code.google.com/p/spitfire/

    ReplyDelete
    Replies
    1. I have updated bigtable source code to include Spitfire.

      Delete
  6. Is wheezy.template compatible with Google App Engine?

    ReplyDelete
    Replies
    1. I'd like to know this as well. Although since Google App Engine works with any WSGI compatible frameworks it should work.

      Delete
  7. Hi, maybe i'll have more success here. I can't figure out how to use a variable as a key in whezzy:

    http://stackoverflow.com/questions/21760517/wheezy-template-dictionary-variable-lookup

    ReplyDelete
  8. hi, can you take a look at new bottle template: simpletemplate

    ReplyDelete
  9. Did you turn on Django template caching? Note that by default it is turned off whereas some of the other template systems it is turned on. See https://docs.djangoproject.com/en/1.7/topics/performance/#the-cached-template-loader

    ReplyDelete
    Replies
    1. The benchmark exclude template loading. It uses template directly, so it is instantiated once and render function is used. See here: https://bitbucket.org/akorn/wheezy.template/src/tip/demos/bigtable/bigtable.py#cl-268

      Delete