Skip to content

Instantly share code, notes, and snippets.

@samuelcolvin
Created September 14, 2017 11:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save samuelcolvin/8362662a9c3aade012bc2e107024287c to your computer and use it in GitHub Desktop.
Save samuelcolvin/8362662a9c3aade012bc2e107024287c to your computer and use it in GitHub Desktop.
aiohttp async template performance
~ 0 23ms ➤ wrk -t 10 -c 100 -d 10 http://localhost:8000/ # master
Running 10s test @ http://localhost:8000/
10 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 166.16ms 8.92ms 242.51ms 97.42%
Req/Sec 62.68 28.98 101.00 56.98%
5973 requests in 10.01s, 219.57MB read
Requests/sec: 596.69
Transfer/sec: 21.93MB
~ 0 10.02s ➤ wrk -t 10 -c 100 -d 10 http://localhost:8000/ # enable_async=False
Running 10s test @ http://localhost:8000/
10 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 166.76ms 8.97ms 243.25ms 95.75%
Req/Sec 62.09 29.77 101.00 56.58%
5954 requests in 10.01s, 218.87MB read
Requests/sec: 594.52
Transfer/sec: 21.85MB
~ 0 10.03s ➤ wrk -t 10 -c 100 -d 10 http://localhost:8000/ # enable_async=True
Running 10s test @ http://localhost:8000/
10 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 446.02ms 42.20ms 536.24ms 96.04%
Req/Sec 30.18 23.44 90.00 72.06%
2195 requests in 10.01s, 80.69MB read
Requests/sec: 219.19
Transfer/sec: 8.06MB
~ 0 10.03s ➤ wrk -t 10 -c 100 -d 10 http://localhost:8000/ # enable_async=True with StreamResponse
Running 10s test @ http://localhost:8000/
10 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.07s 548.73ms 1.95s 57.89%
Req/Sec 6.01 3.35 10.00 53.54%
99 requests in 10.01s, 6.83MB read
Socket errors: connect 0, read 0, write 0, timeout 80
Requests/sec: 9.89
Transfer/sec: 698.38KB
~ 0 10.03s ➤
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
{% if responsive and not responsive.request_desktop %}
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
{% endif %}
{% block extra_meta %}
<meta name="description" content="Tutoring Business Management Software">
<meta name="author" content="TutorCruncher">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
{% endblock %}
<title>{{ title }}</title>
</head>
<body>
{% block body %}{% endblock %}
<script async src='https://www.google-analytics.com/analytics.js'></script>
</body>
</html>
{% extends 'index.jinja' %}
{% block body %}
{% if not bar %}
<div class="section" id="basics">
<h2>Basics<a class="headerlink" href="#basics" title="Permalink to this headline">¶</a></h2>
<p>Jinja2 uses a central object called the template <a class="reference internal" href="#jinja2.Environment" title="jinja2.Environment"><code class="xref py py-class docutils literal"><span class="pre">Environment</span></code></a>.
Instances of this class are used to store the configuration and global objects,
and are used to load templates from the file system or other locations.
Even if you are creating templates from strings by using the constructor of
<a class="reference internal" href="#jinja2.Template" title="jinja2.Template"><code class="xref py py-class docutils literal"><span class="pre">Template</span></code></a> class, an environment is created automatically for you,
albeit a shared one.</p>
{% endif %}
<p>Most applications will create one <a class="reference internal" href="#jinja2.Environment" title="jinja2.Environment"><code class="xref py py-class docutils literal"><span class="pre">Environment</span></code></a> object on application
initialization and use that to load templates. In some cases however, it’s
useful to have multiple environments side by side, if different configurations
are in use.</p>
<p>The simplest way to configure Jinja2 to load templates for your application
looks roughly like this:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">jinja2</span> <span class="k">import</span> <span class="n">Environment</span><span class="p">,</span> <span class="n">PackageLoader</span><span class="p">,</span> <span class="n">select_autoescape</span>
<span class="n">env</span> <span class="o">=</span> <span class="n">Environment</span><span class="p">(</span>
<span class="n">loader</span><span class="o">=</span><span class="n">PackageLoader</span><span class="p">(</span><span class="s1">'yourapplication'</span><span class="p">,</span> <span class="s1">'templates'</span><span class="p">),</span>
<span class="n">autoescape</span><span class="o">=</span><span class="n">select_autoescape</span><span class="p">([</span><span class="s1">'html'</span><span class="p">,</span> <span class="s1">'xml'</span><span class="p">])</span>
<span class="p">)</span>
</pre></div>
</div>
<p>This will create a template environment with the default settings and a
loader that looks up the templates in the <cite>templates</cite> folder inside the
<cite>yourapplication</cite> python package. Different loaders are available
and you can also write your own if you want to load templates from a
database or other resources. This also enables autoescaping for HTML and
XML files.</p>
<p>To load a template from this environment you just have to call the
<code class="xref py py-meth docutils literal"><span class="pre">get_template()</span></code> method which then returns the loaded <a class="reference internal" href="#jinja2.Template" title="jinja2.Template"><code class="xref py py-class docutils literal"><span class="pre">Template</span></code></a>:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="n">template</span> <span class="o">=</span> <span class="n">env</span><span class="o">.</span><span class="n">get_template</span><span class="p">(</span><span class="s1">'mytemplate.html'</span><span class="p">)</span>
</pre></div>
</div>
<p>To render it with some variables, just call the <code class="xref py py-meth docutils literal"><span class="pre">render()</span></code> method:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="nb">print</span> <span class="n">template</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="n">the</span><span class="o">=</span><span class="s1">'variables'</span><span class="p">,</span> <span class="n">go</span><span class="o">=</span><span class="s1">'here'</span><span class="p">)</span>
</pre></div>
</div>
<p>Using a template loader rather than passing strings to <a class="reference internal" href="#jinja2.Template" title="jinja2.Template"><code class="xref py py-class docutils literal"><span class="pre">Template</span></code></a>
or <a class="reference internal" href="#jinja2.Environment.from_string" title="jinja2.Environment.from_string"><code class="xref py py-meth docutils literal"><span class="pre">Environment.from_string()</span></code></a> has multiple advantages. Besides being
a lot easier to use it also enables template inheritance.</p>
<div class="admonition-notes-on-autoescaping admonition">
<p class="first admonition-title">Notes on Autoescaping</p>
<p class="last">In future versions of Jinja2 we might enable autoescaping by default
for security reasons. As such you are encouraged to explicitly
configure autoescaping now instead of relying on the default.</p>
</div>
</div>
<p>Length: {{ data|length }}</p>
<ul>
{% for v in data %}
<li>
{% for word in v %}{{ word }} {% endfor %}
</li>
{% endfor %}
</ul>
{% endblock %}
import random
from pathlib import Path
import jinja2
from aiohttp import web
import aiohttp_jinja2
WORDS = Path('/usr/share/dict/words').read_text().split()
random.seed(a=1)
DATA = [
random.choices(WORDS, k=random.randint(10, max(12, v))) for v in range(100)
]
@aiohttp_jinja2.template('page.jinja')
async def handle(request):
return {
'title': 'Testing',
'data': DATA
}
app = web.Application()
template_dir = Path(__file__).resolve().parent / 'templates'
aiohttp_jinja2.setup(app, enable_async=True, loader=jinja2.FileSystemLoader(str(template_dir)))
app.router.add_get('/', handle)
web.run_app(app, port=8000)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment