changeset 97:ff19219485cc trunk

Add benchmark that builds a large HTML table using different templating techniques (provided by Jonas).
author cmlenz
date Fri, 21 Jul 2006 11:56:01 +0000
parents fa08aef181a2
children 44af12832c5a
files examples/bench/basic.py examples/bench/bigtable.py examples/bench/run.py
diffstat 3 files changed, 312 insertions(+), 160 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/examples/bench/basic.py
@@ -0,0 +1,160 @@
+from cgi import escape
+import os
+from StringIO import StringIO
+import sys
+import timeit
+
+__all__ = ['clearsilver', 'django', 'kid', 'markup', 'simpletal']
+
+def markup(dirname, verbose=False):
+    from markup.template import Context, TemplateLoader
+    loader = TemplateLoader([dirname], auto_reload=False)
+    template = loader.load('template.html')
+    def render():
+        ctxt = Context(title='Just a test', user='joe',
+                       items=['Number %d' % num for num in range(1, 15)])
+        return template.generate(ctxt).render('html')
+
+    if verbose:
+        print render()
+    return render
+
+def cheetah(dirname, verbose=False):
+    # FIXME: infinite recursion somewhere... WTF?
+    from Cheetah.Template import Template
+    class MyTemplate(Template):
+        def serverSidePath(self, path): return os.path.join(dirname, path)
+    filename = os.path.join(dirname, 'template.tmpl')
+    template = MyTemplate(file=filename)
+
+    def render():
+        template = MyTemplate(file=filename,
+                              searchList=[{'title': 'Just a test', 'user': 'joe',
+                                           'items': [u'Number %d' % num for num in range(1, 15)]}])
+        return template.respond()
+
+    if verbose:
+        print render()
+    return render
+
+def clearsilver(dirname, verbose=False):
+    import neo_cgi
+    neo_cgi.update()
+    import neo_util
+    import neo_cs
+    def render():
+        hdf = neo_util.HDF()
+        hdf.setValue('hdf.loadpaths.0', dirname)
+        hdf.setValue('title', escape('Just a test'))
+        hdf.setValue('user', escape('joe'))
+        for num in range(1, 15):
+            hdf.setValue('items.%d' % (num - 1), escape('Number %d' % num))
+        cs = neo_cs.CS(hdf)
+        cs.parseFile('template.cs')
+        return cs.render()
+
+    if verbose:
+        print render()
+    return render
+
+def django(dirname, verbose=False):
+    from django.conf import settings
+    settings.configure(TEMPLATE_DIRS=[os.path.join(dirname, 'templates')])
+    from django import template, templatetags
+    from django.template import loader
+    templatetags.__path__.append(os.path.join(dirname, 'templatetags'))
+    tmpl = loader.get_template('template.html')
+
+    def render():
+        data = {'title': 'Just a test', 'user': 'joe',
+                'items': ['Number %d' % num for num in range(1, 15)]}
+        return tmpl.render(template.Context(data))
+
+    if verbose:
+        print render()
+    return render
+
+def kid(dirname, verbose=False):
+    import kid
+    kid.path = kid.TemplatePath([dirname])
+    template = kid.Template(file='template.kid')
+    def render():
+        template = kid.Template(file='template.kid',
+                                title='Just a test', user='joe',
+                                items=['Number %d' % num for num in range(1, 15)])
+        return template.serialize(output='xhtml')
+
+    if verbose:
+        print render()
+    return render
+
+def nevow(dirname, verbose=False):
+    # FIXME: can't figure out the API
+    from nevow.loaders import xmlfile
+    template = xmlfile('template.xml', templateDir=dirname).load()
+    def render():
+        print template
+
+    if verbose:
+        print render()
+    return render
+
+def simpletal(dirname, verbose=False):
+    from simpletal import simpleTAL, simpleTALES
+    fileobj = open(os.path.join(dirname, 'base.html'))
+    base = simpleTAL.compileHTMLTemplate(fileobj)
+    fileobj.close()
+    fileobj = open(os.path.join(dirname, 'template.html'))
+    template = simpleTAL.compileHTMLTemplate(fileobj)
+    fileobj.close()
+    def render():
+        ctxt = simpleTALES.Context()
+        ctxt.addGlobal('base', base)
+        ctxt.addGlobal('title', 'Just a test')
+        ctxt.addGlobal('user', 'joe')
+        ctxt.addGlobal('items', ['Number %d' % num for num in range(1, 15)])
+        buf = StringIO()
+        template.expand(ctxt, buf)
+        return buf.getvalue()
+
+    if verbose:
+        print render()
+    return render
+
+def run(engines, verbose=False):
+    basepath = os.path.abspath(os.path.dirname(__file__))
+    for engine in engines:
+        dirname = os.path.join(basepath, engine)
+        if verbose:
+            print '%s:' % engine.capitalize()
+            print '--------------------------------------------------------'
+        else:
+            print '%s:' % engine.capitalize(),
+        t = timeit.Timer(setup='from __main__ import %s; render = %s("%s", %s)'
+                               % (engine, engine, dirname, verbose),
+                         stmt='render()')
+        time = t.timeit(number=2000) / 2000
+        if verbose:
+            print '--------------------------------------------------------'
+        print '%.2f ms' % (1000 * time)
+        if verbose:
+            print '--------------------------------------------------------'
+
+
+if __name__ == '__main__':
+    engines = [arg for arg in sys.argv[1:] if arg[0] != '-']
+    if not engines:
+        engines = __all__
+
+    verbose = '-v' in sys.argv
+
+    if '-p' in sys.argv:
+        import hotshot, hotshot.stats
+        prof = hotshot.Profile("template.prof")
+        benchtime = prof.runcall(run, engines, verbose=verbose)
+        stats = hotshot.stats.load("template.prof")
+        stats.strip_dirs()
+        stats.sort_stats('time', 'calls')
+        stats.print_stats()
+    else:
+        run(engines, verbose=verbose)
new file mode 100644
--- /dev/null
+++ b/examples/bench/bigtable.py
@@ -0,0 +1,152 @@
+# -*- encoding: utf-8 -*-
+# Template language benchmarks
+#
+# Objective: Generate a 1000x10 HTML table as fast as possible.
+#
+# Author: Jonas Borgström <jonas@edgewall.com>
+
+import sys
+import timeit
+
+import cElementTree as cet
+from elementtree import ElementTree as et
+import kid
+from markup.builder import tag
+from markup.template import Context, Template
+import neo_cgi
+import neo_cs
+import neo_util
+
+table = [dict(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10)
+          for x in range(1000)]
+
+markup_tmpl = Template("""
+<table xmlns:py="http://markup.edgewall.org/">
+<tr py:for="row in table">
+<td py:for="c in row.values()" py:content="c"/>
+</tr>
+</table>
+""")
+
+markup_tmpl2 = Template("""
+<table xmlns:py="http://markup.edgewall.org/">$table</table>
+""")
+
+kid_tmpl = kid.Template("""
+<table xmlns:py="http://purl.org/kid/ns#">
+<tr py:for="row in table">
+<td py:for="c in row.values()" py:content="c"/>
+</tr>
+</table>
+""")
+
+kid_tmpl2 = kid.Template("""
+<html xmlns:py="http://purl.org/kid/ns#">$table</html>
+""")
+
+
+def test_markup():
+    """Markup template"""
+    ctxt = Context(table=table)
+    stream = markup_tmpl.generate(ctxt)
+    stream.render('html')
+
+def test_markup_builder():
+    """Markup template + tag builder"""
+    stream = tag.TABLE([
+        tag.tr([tag.td(c) for c in row.values()])
+        for row in table
+    ]).generate()
+    ctxt = Context(table=stream)
+    stream = markup_tmpl2.generate(ctxt)
+    stream.render('html')
+
+def test_builder():
+    """Markup tag builder"""
+    stream = tag.TABLE([
+        tag.tr([
+            tag.td(c) for c in row.values()
+        ])
+        for row in table
+    ]).generate()
+    stream.render('html')
+
+def test_kid():
+    """Kid template"""
+    kid_tmpl.table = table
+    kid_tmpl.serialize(output='html')
+
+def test_kid_et():
+    """Kid template + cElementTree"""
+    _table = cet.Element('table')
+    for row in table:
+        td = cet.SubElement(_table, 'tr')
+        for c in row.values():
+            cet.SubElement(td, 'td').text=str(c)
+    kid_tmpl2.table = _table
+    kid_tmpl2.serialize(output='html')
+
+def test_et(): 
+    """ElementTree"""
+    _table = et.Element('table')
+    for row in table:
+        tr = et.SubElement(_table, 'tr')
+        for c in row.values():
+            et.SubElement(tr, 'td').text=str(c)
+    et.tostring(_table)
+        
+def test_cet(): 
+    """cElementTree"""
+    _table = cet.Element('table')
+    for row in table:
+        tr = cet.SubElement(_table, 'tr')
+        for c in row.values():
+            cet.SubElement(tr, 'td').text=str(c)
+    cet.tostring(_table)
+
+def test_clearsilver():
+    """ClearSilver"""
+    hdf = neo_util.HDF()
+    for i, row in enumerate(table):
+        for j, c in enumerate(row.values()):
+            hdf.setValue("rows.%d.cell.%d" % (i, j), str(c))
+
+    cs = neo_cs.CS(hdf)
+    cs.parseStr("""
+<table><?cs
+  each:row=rows
+?><tr><?cs each:c=row.cell
+  ?><td><?cs var:c ?></td><?cs /each
+?></tr><?cs /each?>
+</table>""")
+    cs.render()
+
+
+def run(which=None, number=10):
+    tests = ['test_builder', 'test_markup', 'test_markup_builder', 'test_kid',
+             'test_kid_et', 'test_et', 'test_cet', 'test_clearsilver']
+    if which:
+        tests = filter(lambda n: n[5:] in which, tests)
+
+    for test in tests:
+        t = timeit.Timer(setup='from __main__ import %s;' % test,
+                         stmt='%s()' % test)
+        time = t.timeit(number=number) / number
+
+        print '%-35s %8.2f ms' % (getattr(sys.modules[__name__], test).__doc__,
+                                  1000 * time)
+
+
+if __name__ == '__main__':
+    which = [arg for arg in sys.argv[1:] if arg[0] != '-']
+
+    if '-p' in sys.argv:
+        import hotshot, hotshot.stats
+        prof = hotshot.Profile("template.prof")
+        benchtime = prof.runcall(run, which, number=1)
+        stats = hotshot.stats.load("template.prof")
+        stats.strip_dirs()
+        stats.sort_stats('time', 'calls')
+        stats.print_stats()
+    else:
+        run(which)
deleted file mode 100644
--- a/examples/bench/run.py
+++ /dev/null
@@ -1,160 +0,0 @@
-from cgi import escape
-import os
-from StringIO import StringIO
-import sys
-import timeit
-
-__all__ = ['clearsilver', 'django', 'kid', 'markup', 'simpletal']
-
-def markup(dirname, verbose=False):
-    from markup.template import Context, TemplateLoader
-    loader = TemplateLoader([dirname], auto_reload=False)
-    template = loader.load('template.html')
-    def render():
-        ctxt = Context(title='Just a test', user='joe',
-                       items=['Number %d' % num for num in range(1, 15)])
-        return template.generate(ctxt).render('html')
-
-    if verbose:
-        print render()
-    return render
-
-def cheetah(dirname, verbose=False):
-    # FIXME: infinite recursion somewhere... WTF?
-    from Cheetah.Template import Template
-    class MyTemplate(Template):
-        def serverSidePath(self, path): return os.path.join(dirname, path)
-    filename = os.path.join(dirname, 'template.tmpl')
-    template = MyTemplate(file=filename)
-
-    def render():
-        template = MyTemplate(file=filename,
-                              searchList=[{'title': 'Just a test', 'user': 'joe',
-                                           'items': [u'Number %d' % num for num in range(1, 15)]}])
-        return template.respond()
-
-    if verbose:
-        print render()
-    return render
-
-def clearsilver(dirname, verbose=False):
-    import neo_cgi
-    neo_cgi.update()
-    import neo_util
-    import neo_cs
-    def render():
-        hdf = neo_util.HDF()
-        hdf.setValue('hdf.loadpaths.0', dirname)
-        hdf.setValue('title', escape('Just a test'))
-        hdf.setValue('user', escape('joe'))
-        for num in range(1, 15):
-            hdf.setValue('items.%d' % (num - 1), escape('Number %d' % num))
-        cs = neo_cs.CS(hdf)
-        cs.parseFile('template.cs')
-        return cs.render()
-
-    if verbose:
-        print render()
-    return render
-
-def django(dirname, verbose=False):
-    from django.conf import settings
-    settings.configure(TEMPLATE_DIRS=[os.path.join(dirname, 'templates')])
-    from django import template, templatetags
-    from django.template import loader
-    templatetags.__path__.append(os.path.join(dirname, 'templatetags'))
-    tmpl = loader.get_template('template.html')
-
-    def render():
-        data = {'title': 'Just a test', 'user': 'joe',
-                'items': ['Number %d' % num for num in range(1, 15)]}
-        return tmpl.render(template.Context(data))
-
-    if verbose:
-        print render()
-    return render
-
-def kid(dirname, verbose=False):
-    import kid
-    kid.path = kid.TemplatePath([dirname])
-    template = kid.Template(file='template.kid')
-    def render():
-        template = kid.Template(file='template.kid',
-                                title='Just a test', user='joe',
-                                items=['Number %d' % num for num in range(1, 15)])
-        return template.serialize(output='xhtml')
-
-    if verbose:
-        print render()
-    return render
-
-def nevow(dirname, verbose=False):
-    # FIXME: can't figure out the API
-    from nevow.loaders import xmlfile
-    template = xmlfile('template.xml', templateDir=dirname).load()
-    def render():
-        print template
-
-    if verbose:
-        print render()
-    return render
-
-def simpletal(dirname, verbose=False):
-    from simpletal import simpleTAL, simpleTALES
-    fileobj = open(os.path.join(dirname, 'base.html'))
-    base = simpleTAL.compileHTMLTemplate(fileobj)
-    fileobj.close()
-    fileobj = open(os.path.join(dirname, 'template.html'))
-    template = simpleTAL.compileHTMLTemplate(fileobj)
-    fileobj.close()
-    def render():
-        ctxt = simpleTALES.Context()
-        ctxt.addGlobal('base', base)
-        ctxt.addGlobal('title', 'Just a test')
-        ctxt.addGlobal('user', 'joe')
-        ctxt.addGlobal('items', ['Number %d' % num for num in range(1, 15)])
-        buf = StringIO()
-        template.expand(ctxt, buf)
-        return buf.getvalue()
-
-    if verbose:
-        print render()
-    return render
-
-def run(engines, verbose=False):
-    basepath = os.path.abspath(os.path.dirname(__file__))
-    for engine in engines:
-        dirname = os.path.join(basepath, engine)
-        if verbose:
-            print '%s:' % engine.capitalize()
-            print '--------------------------------------------------------'
-        else:
-            print '%s:' % engine.capitalize(),
-        t = timeit.Timer(setup='from __main__ import %s; render = %s("%s", %s)'
-                               % (engine, engine, dirname, verbose),
-                         stmt='render()')
-        time = t.timeit(number=2000) / 2000
-        if verbose:
-            print '--------------------------------------------------------'
-        print '%.2f ms' % (1000 * time)
-        if verbose:
-            print '--------------------------------------------------------'
-
-
-if __name__ == '__main__':
-    engines = [arg for arg in sys.argv[1:] if arg[0] != '-']
-    if not engines:
-        engines = __all__
-
-    verbose = '-v' in sys.argv
-
-    if '-p' in sys.argv:
-        import hotshot, hotshot.stats
-        prof = hotshot.Profile("template.prof")
-        benchtime = prof.runcall(run, engines, verbose=verbose)
-        stats = hotshot.stats.load("template.prof")
-        stats.strip_dirs()
-        stats.sort_stats('time', 'calls')
-        stats.print_stats()
-    else:
-        run(engines, verbose=verbose)
Copyright (C) 2012-2017 Edgewall Software