Permalink
Browse files

Adding benchmarks for Chameleon and Mako (in the bm_xxx.py format). T…

…hese are suitable for inclusion in the PyPy speed tool.
  • Loading branch information...
malthe committed Sep 29, 2011
1 parent 619bf1f commit bd7a665923fedc2191147f54b124b2e5d0818ddf
Showing with 332 additions and 0 deletions.
  1. +128 −0 benchmarks/bm_chameleon.py
  2. +153 −0 benchmarks/bm_mako.py
  3. +51 −0 benchmarks/util.py
View
@@ -0,0 +1,128 @@
+#!/usr/bin/python2
+
+"""
+Benchmark for test the performance of Chameleon page template engine.
+"""
+
+__author__ = "mborch@gmail.com (Malthe Borch)"
+
+# Python imports
+import os
+import sys
+import optparse
+import time
+
+# Local imports
+import util
+
+
+def relative(*args):
+ return os.path.join(os.path.dirname(os.path.abspath(__file__)), *args)
+
+sys.path.insert(0, relative('..', 'src'))
+
+# Chameleon imports
+from chameleon import PageTemplate
+
+
+LOREM_IPSUM = """Quisque lobortis hendrerit posuere. Curabitur
+aliquet consequat sapien molestie pretium. Nunc adipiscing luc
+tus mi, viverra porttitor lorem vulputate et. Ut at purus sem,
+sed tincidunt ante. Vestibulum ante ipsum primis in faucibus
+orci luctus et ultrices posuere cubilia Curae; Praesent pulvinar
+sodales justo at congue. Praesent aliquet facilisis nisl a
+molestie. Sed tempus nisl ut augue eleifend tincidunt. Sed a
+lacinia nulla. Cras tortor est, mollis et consequat at,
+vulputate et orci. Nulla sollicitudin"""
+
+BASE_TEMPLATE = '''
+<tal:macros condition="False">
+ <table metal:define-macro="table">
+ <tr tal:repeat="row table">
+ <td tal:repeat="col row">${col}</td>
+ </tr>
+ </table>
+ <img metal:define-macro="img" src="${src}" alt="${alt}" />
+</tal:macros>
+<html metal:define-macro="master">
+ <head><title>${title.strip()}</title></head>
+ <body metal:define-slot="body" />
+</html>
+'''
+
+PAGE_TEMPLATE = '''
+<html metal:define-macro="master" metal:extend-macro="base.macros['master']">
+<body metal:fill-slot="body">
+<table metal:use-macro="base.macros['table']" />
+images:
+<tal:images repeat="nr xrange(img_count)">
+ <img tal:define="src '/foo/bar/baz.png';
+ alt 'no image :o'"
+ metal:use-macro="base.macros['img']" />
+</tal:images>
+<metal:body define-slot="body" />
+<p tal:repeat="nr paragraphs">${lorem}</p>
+<table metal:use-macro="base.macros['table']" />
+</body>
+</html>
+'''
+
+CONTENT_TEMPLATE = '''
+<html metal:use-macro="page.macros['master']">
+<span metal:define-macro="fun1">fun1</span>
+<span metal:define-macro="fun2">fun2</span>
+<span metal:define-macro="fun3">fun3</span>
+<span metal:define-macro="fun4">fun4</span>
+<span metal:define-macro="fun5">fun5</span>
+<span metal:define-macro="fun6">fun6</span>
+<body metal:fill-slot="body">
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+Nam laoreet justo in velit faucibus lobortis. Sed dictum sagittis
+volutpat. Sed adipiscing vestibulum consequat. Nullam laoreet, ante
+nec pretium varius, libero arcu porttitor orci, id cursus odio nibh
+nec leo. Vestibulum dapibus pellentesque purus, sed bibendum tortor
+laoreet id. Praesent quis sodales ipsum. Fusce ut ligula sed diam
+pretium sagittis vel at ipsum. Nulla sagittis sem quam, et volutpat
+velit. Fusce dapibus ligula quis lectus ultricies tempor. Pellente</p>
+<span metal:use-macro="template.macros['fun1']" />
+<span metal:use-macro="template.macros['fun2']" />
+<span metal:use-macro="template.macros['fun3']" />
+<span metal:use-macro="template.macros['fun4']" />
+<span metal:use-macro="template.macros['fun5']" />
+<span metal:use-macro="template.macros['fun6']" />
+</body>
+</html>
+'''
+
+
+def test_mako(count):
+ template = PageTemplate(CONTENT_TEMPLATE)
+ base = PageTemplate(BASE_TEMPLATE)
+ page = PageTemplate(PAGE_TEMPLATE)
+
+ table = [xrange(150) for i in xrange(150)]
+ paragraphs = xrange(50)
+ title = 'Hello world!'
+
+ times = []
+ for i in range(count):
+ t0 = time.time()
+ data = template.render(
+ table=table, paragraphs=paragraphs,
+ lorem=LOREM_IPSUM, title=title,
+ img_count=50,
+ base=base,
+ page=page,
+ )
+ t1 = time.time()
+ times.append(t1-t0)
+ return times
+
+if __name__ == "__main__":
+ parser = optparse.OptionParser(
+ usage="%prog [options]",
+ description=("Test the performance of Chameleon templates."))
+ util.add_standard_options_to(parser)
+ (options, args) = parser.parse_args()
+
+ util.run_benchmark(options, options.num_runs, test_mako)
View
@@ -0,0 +1,153 @@
+#!/usr/bin/python
+
+"""
+Benchmark for test the performance of Mako templates engine.
+Includes:
+ -two template inherences
+ -HTML escaping, XML escaping, URL escaping, whitespace trimming
+ -function defitions and calls
+ -forloops
+"""
+
+__author__ = "virhilo@gmail.com (Lukasz Fidosz)"
+
+# Python imports
+import os
+import sys
+import optparse
+import time
+
+# Local imports
+import util
+
+def relative(*args):
+ return os.path.join(os.path.dirname(os.path.abspath(__file__)), *args)
+
+sys.path.insert(0, relative('..', 'lib'))
+
+# Mako imports
+from mako.template import Template
+from mako.lookup import TemplateLookup
+
+
+LOREM_IPSUM = """Quisque lobortis hendrerit posuere. Curabitur
+aliquet consequat sapien molestie pretium. Nunc adipiscing luc
+tus mi, viverra porttitor lorem vulputate et. Ut at purus sem,
+sed tincidunt ante. Vestibulum ante ipsum primis in faucibus
+orci luctus et ultrices posuere cubilia Curae; Praesent pulvinar
+sodales justo at congue. Praesent aliquet facilisis nisl a
+molestie. Sed tempus nisl ut augue eleifend tincidunt. Sed a
+lacinia nulla. Cras tortor est, mollis et consequat at,
+vulputate et orci. Nulla sollicitudin"""
+
+BASE_TEMPLATE = """
+<%def name="render_table(table)">
+ <table>
+ % for row in table:
+ <tr>
+ % for col in row:
+ <td>${col|h}</td>
+ % endfor
+ </tr>
+ % endfor
+ </table>
+</%def>
+<%def name="img(src, alt)">
+ <img src="${src|u}" alt="${alt}" />
+</%def>
+<html>
+ <head><title>${title|h,trim}</title></head>
+ <body>
+ ${next.body()}
+ </body>
+<html>
+"""
+
+PAGE_TEMPLATE = """
+<%inherit file="base.mako"/>
+<table>
+ % for row in table:
+ <tr>
+ % for col in row:
+ <td>${col}</td>
+ % endfor
+ </tr>
+ % endfor
+</table>
+% for nr in xrange(img_count):
+ ${parent.img('/foo/bar/baz.png', 'no image :o')}
+% endfor
+${next.body()}
+% for nr in paragraphs:
+ <p>${lorem|x}</p>
+% endfor
+${parent.render_table(table)}
+"""
+
+CONTENT_TEMPLATE = """
+<%inherit file="page.mako"/>
+<%def name="fun1()">
+ <span>fun1</span>
+</%def>
+<%def name="fun2()">
+ <span>fun2</span>
+</%def>
+<%def name="fun3()">
+ <span>foo3</span>
+</%def>
+<%def name="fun4()">
+ <span>foo4</span>
+</%def>
+<%def name="fun5()">
+ <span>foo5</span>
+</%def>
+<%def name="fun6()">
+ <span>foo6</span>
+</%def>
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+Nam laoreet justo in velit faucibus lobortis. Sed dictum sagittis
+volutpat. Sed adipiscing vestibulum consequat. Nullam laoreet, ante
+nec pretium varius, libero arcu porttitor orci, id cursus odio nibh
+nec leo. Vestibulum dapibus pellentesque purus, sed bibendum tortor
+laoreet id. Praesent quis sodales ipsum. Fusce ut ligula sed diam
+pretium sagittis vel at ipsum. Nulla sagittis sem quam, et volutpat
+velit. Fusce dapibus ligula quis lectus ultricies tempor. Pellente</p>
+${fun1()}
+${fun2()}
+${fun3()}
+${fun4()}
+${fun5()}
+${fun6()}
+"""
+
+
+def test_mako(count):
+
+ lookup = TemplateLookup()
+ lookup.put_string('base.mako', BASE_TEMPLATE)
+ lookup.put_string('page.mako', PAGE_TEMPLATE)
+
+ template = Template(CONTENT_TEMPLATE, lookup=lookup)
+
+ table = [xrange(150) for i in xrange(150)]
+ paragraphs = xrange(50)
+ title = 'Hello world!'
+
+ times = []
+ for i in range(count):
+ t0 = time.time()
+ data = template.render(table=table, paragraphs=paragraphs,
+ lorem=LOREM_IPSUM, title=title,
+ img_count=50)
+ t1 = time.time()
+ times.append(t1-t0)
+ return times
+
+if __name__ == "__main__":
+ parser = optparse.OptionParser(
+ usage="%prog [options]",
+ description=("Test the performance of Mako templates."))
+ util.add_standard_options_to(parser)
+ (options, args) = parser.parse_args()
+
+ util.run_benchmark(options, options.num_runs, test_mako)
View
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+
+"""Utility code for benchmark scripts."""
+
+__author__ = "collinwinter@google.com (Collin Winter)"
+
+import math
+import operator
+
+
+def run_benchmark(options, num_runs, bench_func, *args):
+ """Run the given benchmark, print results to stdout.
+
+ Args:
+ options: optparse.Values instance.
+ num_runs: number of times to run the benchmark
+ bench_func: benchmark function. `num_runs, *args` will be passed to this
+ function. This should return a list of floats (benchmark execution
+ times).
+ """
+ if options.profile:
+ import cProfile
+ prof = cProfile.Profile()
+ prof.runcall(bench_func, num_runs, *args)
+ prof.print_stats(sort=options.profile_sort)
+ else:
+ data = bench_func(num_runs, *args)
+ if options.take_geo_mean:
+ product = reduce(operator.mul, data, 1)
+ print math.pow(product, 1.0 / len(data))
+ else:
+ for x in data:
+ print x
+
+
+def add_standard_options_to(parser):
+ """Add a bunch of common command-line flags to an existing OptionParser.
+
+ This function operates on `parser` in-place.
+
+ Args:
+ parser: optparse.OptionParser instance.
+ """
+ parser.add_option("-n", action="store", type="int", default=100,
+ dest="num_runs", help="Number of times to run the test.")
+ parser.add_option("--profile", action="store_true",
+ help="Run the benchmark through cProfile.")
+ parser.add_option("--profile_sort", action="store", type="str",
+ default="time", help="Column to sort cProfile output by.")
+ parser.add_option("--take_geo_mean", action="store_true",
+ help="Return the geo mean, rather than individual data.")

0 comments on commit bd7a665

Please sign in to comment.