{"id":1497,"date":"2010-06-04T14:48:53","date_gmt":"2010-06-04T18:48:53","guid":{"rendered":"http:\/\/www.xorad.com\/blog\/?p=1497"},"modified":"2010-11-24T03:01:29","modified_gmt":"2010-11-24T08:01:29","slug":"speeding-up-built-in-django-templates","status":"publish","type":"post","link":"http:\/\/www.xorad.com\/blog\/?p=1497","title":{"rendered":"Speeding up built in Django Templates"},"content":{"rendered":"<div>\n<p>My Django template redering is unbearably slow. I found what I thing is a surprising way to speed up my Django template.<\/p>\n<p>I have a site that has lots of point objects with latitude and longitude. When I render a template with 1500 of these points, and the template contains a for loop to output them, the redering time was about 12 seconds. This of course is an unacceptably long time for loading a fairly simple web page that just happens to have a lot of numbers on it.<\/p>\n<pre class=\"qoate-code\">\r\n{% for point in points %}\r\npolylinePoints.push( new YGeoPoint( {{ point.lat }}, {{ point.lng }} ) );\r\n{% endfor %}\r\n<\/pre>\n<p>I started to play around with ways to speed things up. Using python to format the points into a long string before passing it to the template would cause the template to render in less than 1 second. The problem with this particular optimization is that it transfers part of the responsibility of rendering the data from the template engine to the python code. It would be better if the python code doesn&#8217;t need to know this much about how the data will be rendered.<\/p>\n<p>I decided to try using a python code profiler to see if it would yield some clue as to what is causing the template to render so slowly. Using the information about Django profiling found at\u00c2\u00a0<a href=\"http:\/\/code.djangoproject.com\/wiki\/ProfilingDjango\">http:\/\/code.djangoproject.com\/wiki\/ProfilingDjango<\/a> I got the following profiling data.<\/p>\n<pre style=\"overflow:scroll;font-size:80%\">\r\n618620 function calls (618335 primitive calls) in 15.518 CPU seconds\r\nOrdered by: internal time, call count\r\nList reduced from 425 to 20 due to restriction &lt;20&gt;\r\nncalls \u00c2\u00a0tottime \u00c2\u00a0percall \u00c2\u00a0cumtime \u00c2\u00a0percall filename:lineno(function)\r\n64288 \u00c2\u00a0 \u00c2\u00a02.317 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a02.317 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/posixpath.py:168(exists)\r\n36758 \u00c2\u00a0 \u00c2\u00a01.909 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a09.553 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/utils\/translation\/__init__.py:23(delayed_loader)\r\n18375 \u00c2\u00a0 \u00c2\u00a01.436 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a01.711 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/utils\/importlib.py:18(import_module)\r\n82766 \u00c2\u00a0 \u00c2\u00a01.429 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a01.429 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/posixpath.py:56(join)\r\n9184 \u00c2\u00a0 \u00c2\u00a01.228 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a05.588 \u00c2\u00a0 \u00c2\u00a00.001 \/usr\/lib\/python2.5\/gettext.py:421(find)\r\n9184 \u00c2\u00a0 \u00c2\u00a00.800 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 12.763 \u00c2\u00a0 \u00c2\u00a00.001 \/usr\/lib\/python2.5\/site-packages\/django\/utils\/formats.py:10(get_format_modules)\r\n18370 \u00c2\u00a0 \u00c2\u00a00.613 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.802 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/utils\/translation\/trans_real.py:212(get_language)\r\n91937 \u00c2\u00a0 \u00c2\u00a00.588 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.588 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/utils\/functional.py:274(__getattr__)\r\n9184 \u00c2\u00a0 \u00c2\u00a00.495 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.727 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/gettext.py:130(_expand_lang)\r\n36762 \u00c2\u00a0 \u00c2\u00a00.472 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 10.026 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/utils\/functional.py:54(_curried)\r\n9184 \u00c2\u00a0 \u00c2\u00a00.448 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a06.488 \u00c2\u00a0 \u00c2\u00a00.001 \/usr\/lib\/python2.5\/site-packages\/django\/utils\/translation\/trans_real.py:311(check_for_language)\r\n3054 \u00c2\u00a0 \u00c2\u00a00.346 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.420 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/utils\/numberformat.py:3(format)\r\n9184 \u00c2\u00a0 \u00c2\u00a00.304 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 13.218 \u00c2\u00a0 \u00c2\u00a00.001 \/usr\/lib\/python2.5\/site-packages\/django\/utils\/formats.py:37(get_format)\r\n18368 \u00c2\u00a0 \u00c2\u00a00.274 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.274 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/utils\/translation\/trans_real.py:34(to_locale)\r\n18368 \u00c2\u00a0 \u00c2\u00a00.260 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.260 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/utils\/importlib.py:4(_resolve_name)\r\n9184 \u00c2\u00a0 \u00c2\u00a00.232 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.232 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/locale.py:281(normalize)\r\n9188 \u00c2\u00a0 \u00c2\u00a00.168 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.168 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/posixpath.py:74(split)\r\n1517 \u00c2\u00a0 \u00c2\u00a00.145 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.162 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/db\/models\/base.py:251(__init__)\r\n2 \u00c2\u00a0 \u00c2\u00a00.144 \u00c2\u00a0 \u00c2\u00a00.072 \u00c2\u00a0 14.697 \u00c2\u00a0 \u00c2\u00a07.348 \/usr\/lib\/python2.5\/site-packages\/django\/template\/defaulttags.py:124(render)\r\n3089 \u00c2\u00a0 \u00c2\u00a00.130 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.182 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/template\/__init__.py:710(_resolve_lookup)\r\n<\/pre>\n<p>It looks like Django is spending a lot of time trying to translate, format and\/or localize my numbers. They are just simple floating point latitude and longitude, and are just being written out to be part of some JavaScript code. They don&#8217;t need fancy formatting. \u00c2\u00a0This prompted me to change my loop to the following :<\/p>\n<pre class=\"qoate-code\">\r\n{% for point in points %}\r\npolylinePoints.push( new YGeoPoint( {{ point.lat|stringformat:\"s\" }}, {{ point.lng|stringformat:\"s\" }} ) );\r\n{% endfor %}\r\n<\/pre>\n<p>And re-ran my profiling, with a time of less than 2 seconds for rendering the template.<\/p>\n<pre style=\"overflow:scroll;font-size:80%\">\r\n102767 function calls (102482 primitive calls) in 1.620 CPU seconds\r\nOrdered by: internal time, call count\r\nList reduced from 392 to 20 due to restriction &lt;20&gt;\r\nncalls \u00c2\u00a0tottime \u00c2\u00a0percall \u00c2\u00a0cumtime \u00c2\u00a0percall filename:lineno(function)\r\n3089 \u00c2\u00a0 \u00c2\u00a00.121 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.157 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/template\/__init__.py:710(_resolve_lookup)\r\n3090 \u00c2\u00a0 \u00c2\u00a00.108 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.457 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/template\/__init__.py:550(resolve)\r\n3034 \u00c2\u00a0 \u00c2\u00a00.105 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.105 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/template\/defaultfilters.py:230(stringformat)\r\n1517 \u00c2\u00a0 \u00c2\u00a00.090 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.107 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/db\/models\/base.py:251(__init__)\r\n2 \u00c2\u00a0 \u00c2\u00a00.089 \u00c2\u00a0 \u00c2\u00a00.045 \u00c2\u00a0 \u00c2\u00a01.101 \u00c2\u00a0 \u00c2\u00a00.550 \/usr\/lib\/python2.5\/site-packages\/django\/template\/defaulttags.py:124(render)\r\n1517 \u00c2\u00a0 \u00c2\u00a00.081 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.111 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/db\/backends\/mysql\/compiler.py:4(resolve_columns)\r\n3076 \u00c2\u00a0 \u00c2\u00a00.079 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.186 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/utils\/formats.py:77(localize)\r\n18\/1 \u00c2\u00a0 \u00c2\u00a00.074 \u00c2\u00a0 \u00c2\u00a00.004 \u00c2\u00a0 \u00c2\u00a01.144 \u00c2\u00a0 \u00c2\u00a01.144 \/usr\/lib\/python2.5\/site-packages\/django\/template\/__init__.py:792(render)\r\n16994 \u00c2\u00a0 \u00c2\u00a00.071 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.079 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/utils\/encoding.py:54(force_unicode)\r\n3076 \u00c2\u00a0 \u00c2\u00a00.068 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.888 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/template\/debug.py:87(render)\r\n3076 \u00c2\u00a0 \u00c2\u00a00.064 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.110 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/utils\/html.py:30(escape)\r\n3099 \u00c2\u00a0 \u00c2\u00a00.056 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.166 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/utils\/functional.py:254(wrapper)\r\n6134 \u00c2\u00a0 \u00c2\u00a00.055 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.055 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/utils\/safestring.py:89(mark_safe)\r\n1517 \u00c2\u00a0 \u00c2\u00a00.045 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.045 \u00c2\u00a0 \u00c2\u00a00.000 \/var\/lib\/python-support\/python2.5\/MySQLdb\/times.py:43(DateTime_or_None)\r\n1518 \u00c2\u00a0 \u00c2\u00a00.039 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.429 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/db\/models\/query.py:213(iterator)\r\n3090 \u00c2\u00a0 \u00c2\u00a00.036 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.036 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/template\/context.py:41(__getitem__)\r\n3094 \u00c2\u00a0 \u00c2\u00a00.033 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.190 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/template\/__init__.py:692(resolve)\r\n1 \u00c2\u00a0 \u00c2\u00a00.029 \u00c2\u00a0 \u00c2\u00a00.029 \u00c2\u00a0 \u00c2\u00a00.075 \u00c2\u00a0 \u00c2\u00a00.075 \/var\/lib\/python-support\/python2.5\/MySQLdb\/cursors.py:282(_fetch_row)\r\n3951 \u00c2\u00a0 \u00c2\u00a00.025 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.025 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/utils\/functional.py:274(__getattr__)\r\n1518 \u00c2\u00a0 \u00c2\u00a00.022 \u00c2\u00a0 \u00c2\u00a00.000 \u00c2\u00a0 \u00c2\u00a00.022 \u00c2\u00a0 \u00c2\u00a00.000 \/usr\/lib\/python2.5\/site-packages\/django\/db\/utils.py:122(_route_db)\r\n<\/pre>\n<p>{{ point.lat }} takes much more time than {{ point.lat|stringformat:&#8221;s&#8221; }}<\/p>\n<p>Let me repeat that.<\/p>\n<p><strong>{{ point.lat }} takes much more time than {{ point.lat|stringformat:&#8221;s&#8221; }}<\/strong><\/p>\n<p>So the result that I found to be rather surprising is that by using the stringformat function on my floating point numbers I was able to drastically speed up the rendering of my latitude and longitudes in by Django template. It seems that by explicitly telling it to use stringformat, I cause it to skip all sorts of number formatting code that is not necessary when you are simply producing JavaScript output.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>My Django template redering is unbearably slow. I found what I thing is a surprising way to speed up my Django template. I have a site that has lots of point objects with latitude and longitude. When I render a &hellip; <a href=\"http:\/\/www.xorad.com\/blog\/?p=1497\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[14],"tags":[],"class_list":["post-1497","post","type-post","status-publish","format-standard","hentry","category-django"],"_links":{"self":[{"href":"http:\/\/www.xorad.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1497","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.xorad.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.xorad.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.xorad.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/www.xorad.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1497"}],"version-history":[{"count":8,"href":"http:\/\/www.xorad.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1497\/revisions"}],"predecessor-version":[{"id":1502,"href":"http:\/\/www.xorad.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1497\/revisions\/1502"}],"wp:attachment":[{"href":"http:\/\/www.xorad.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1497"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.xorad.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1497"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.xorad.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1497"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}