Tuesday, November 23, 2010

Profiling My Slow Django Templates

This is another techie post. If you have no interest in Django or Python then you may not be interested in this post either :-)

A Django app that I'm working on was running ridiculously slow. A quick Google search helped me to learn the basics of the Python profiler.

Add the following in order to profile my_function()


cProfile.runctx("my_function()", globals(), locals(), filename="profile.txt")


Run the code in question and then run the following in the Python interpreter in order to analyse the results:


import pstats
p = pstats.Stats("profile.txt")
p.sort_stats("cumulative").print_stats()


This will output results similar to the following, sorted in order of cumulative execution time:


Wed Nov 10 07:39:39 2010 profile.txt

11405 function calls (11319 primitive calls) in 0.459 CPU seconds

Ordered by: cumulative time

ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.458 0.458 /home/chris/MyProject/thingie.py:60(my_function)
1 0.001 0.001 0.458 0.458 /home/chris/MyProject/wizzle.py:32(thing_function)
1 0.000 0.000 0.358 0.358 /home/chris/MyProject/hoss.py:2(hussss)




In my case, I needed to see the call graph. The following draw a pretty .png picture of the call graph

gprof2dot -f pstats profile.txt | dot -Tpng -o out2.png

Armed with a visualisation of all this data, it became apparent that Django's template system was taking up most of the time. I had a line in a template, deep within a doubly nested for-loop that looked like this:

onclick="leftClick( {{cell.x}}, {{cell.y}} )"

Django was rendering this very slowly indeed. I ended up removing the for loop from the template and generating the html directly from within python code. This wasn't pretty but sped things up by nearly an order of magnitude.

I wonder if there is a more satisfactory solution to this problem, other than dumping Django templates altogether.

6 comments:

jonathanj said...

Hmmmm... I don't know about the Django templates, or why that line would take ages, but it's best not to use onclick, anyway.
Assuming this is for Swinemeeper, just have each square as a link:
< td >< a href="whatever-the-url-is">< img alt="mine" />

Then add JavaScript stuff later.

Oh. I wonder if it's going to render that HTML as HTML :S

jonathanj said...

I added some spaces to make it work, but the closing 'a' and 'td' tags went missing.

Anonymous said...

um, arn't you on holiday?

Why are you still thinking about techie stuff?

Chris said...

Thanks for the tips, Jeff. I replaced all that event handling stuff and now use JQuery to add event handlers instead. It still kind of sucks, though. I want it to be able to respond to left click, double click and right click but I'm not really sure how to do that in a clean way. I'll send you an email or something some time.

Anonymous, I am trying to learn how to do web development while we are away. It turns out to be quite tricky.

jonathanj said...

I'm not sure if anonymous was talking to me or Chris, but yeah, we're both on holiday.

Chris, I wrote you an email relating to this a few days ago, but it doesn't seem to be sending. You might have to wait until I get home for me to be able to send it.

jonathanj said...

Actually, I'll just paste it in here:

###################################

Hey. It should be pretty easy, if you use jQuery or something.
I can write it for you, if you like. Or just give you some pointers.

It's best to finish off the functionality without using JS, then add JS in after.
Get it so that it only does the JSON if you're passing through a particular GET or POST parameter (eg 'ajax').

Did you try that Alwaysdata? If you want me to have a look at it, try putting it on there.
Or save the game page as an HTML file, so I can have a look at it.
####################################