2011-06-27

HTML5 Canvas performance: is it possible to avoid a full redraw with a moving background?

Playing the multiplayer flash game Realm of the Mad God I was wondering how to implement map scrolling with Canvas. Knowing how slow Canvas draw images, the simple approach of redrawing everything for every frame sounded quite inneficient.

So using the javascript library called Sprites.js I created what I called a map scroller script that use 9 half-size canvas surface to draw the scrolling background and then move those canvas in the same direction to create the scrolling effect. Here is the 2 versions to compare:

Then I monitored the CPU usage from different browsers. This table represent the total load of the system created by the animation on my linux box. (B:Browser load, X:Xorg load)

BrowserCanvas system loadScrolling system load
Chromium 12.066% (B:58%, X:7%)38% (B:20%,X:18%)
Opera 11.50 beta104% (B:90%, X:14%)49% (B:34%, X:15%)
Firefox 5.086% (B:62%, X:24%)42% (B:21%, X:20%)

Some of the load seeme to be shifted to the XOrg server when the scolling version is used. I assume that XOrg has to do more composition work with the 9 canvas. Despite this there is a clear decrease in total system load.

The perfomance increase given by 1 - (Scrolling load / Canvas load) is the following:

BrowserPerformance increase
Chromium 12.042%
Opera 11.50 beta52%
Firefox 5.032%

So it seems that this method is a valid way to yield a nice performance boost in all browser for a tile scroller game or any parralax effect. It also highlight the slowliness of the Canvas API when it comes to drawing images.

I will release the this code soon as part as the Sprite.js library.

2010-03-10

Apache mpm_worker and mpm_prefork with Python/Django

Although the Django documentation clearly states that mpm_prefork is the way to go we decided to give mpm_workers with mod_wsgi a try. Django 1.1.1 is thread safe so why not? Also there is some good reasons why people wants to use mpm_worker instead of mpm_prefork.

Our website get about 100 requests by second spread on 3 frontend servers. That's really not much. First it went well. Then, when traffic increased a bit, everything started to slow down. It was slowing so much that the site was almost unusable. A little bit of profiling showed that a lot of CPU time was spent into some Django's template filters. We tried to use a Django template caching technics but it didn't helped much.

At the time the mpm_worker config was something like that:

ServerLimit 16
StartServers 2
MaxClients 150
MinSpareThreads 25
MaxSpareThreads 75
ThreadsPerChild 25

Python is known to not do well with threading because of the GIL so I try to experiment a bit with these values. I try to reduce the ThreadsPerChild value and it's what I found:

Value of ThreadsPerChildAverage response time in millisecondes
20.13
40.16
80.19
160.21

The performance improvement is clear. With less threads, the CPU usage was going down and the server started to feel a lot more snappy. These numbers don't even reflect how bad the situation was with more traffic. A single server was chocking with less than 100 requests by second.

Conclusion

If you really have to use mpm_worker with python code, make sure to test everything under high concurrent load. If the performances are bad, reduce the value of ThreadsPerChild. I would recommend 2-5 threads maximum.

2009-03-07

Django page CMS 0.2.4 is released

A lot of time has passed since the last time I blogged about my Django CMS

Django page CMS has gained users and contributors since this moment. I takes me more time to checking out the new issues, keep them as low as possible and have an up to date documentation. It begins to be difficult to handle everything myself with my new job at Opera software. Thanks for the contributors that help me in this task.

The code base is now quite stable. I try to keep it clean and simple. It's not always easy with all the feature proposition and code contribution. Here is a brief summary of what have been achieved since the version 0.2.1 up to lastest 0.2.4 release :

  • I have completly revamped the page admin interface. Now the tree is collapsed by default. When you expand a node, the children are loaded via an XMLHttpRequest. This method reduce the amount of SQL request needed by default. Before the tree was reladed each time you move a page in the tree. Now only the relevants parts are reloaded.s
  • The site framework is now optional and disabled by default. It's also implemented in a simpler way.
  • I added a basic way to integrate third party application into the page edition form.
  • I also try to make things a little faster by using cache on page content. It's quite easy to invalidate the content cache of a page after modification. For nested pages and everything related to URLs it's a little bit more complicated.
  • The different languages support is now stronger and tested. You can use languages code up to 5 letters and redirect client special languages to others like "fr-ch" to "fr" (Swiss french to simple french).
  • I try to add a test everytime it was relevant and if the bug/feature was suggest to regression.
  • Finaly, I learned how to do a proper python package and published it on PyPi. Install django page cms is as simple as : $ sudo pip install django-page-cms.

My goal is to offer a very simple, easy to start with Django CMS. I try to add the important features to reduce the need of forks. But I know how it's not possible to please everyone. (django-cms 2.0 is one of the fork)

For the near future I plan to break some minor stuff so if you don't want any trouble, I recommend to stick to the 0.2.4 version.

Thanks again a lot to the contributors and users. I hope you will continue to use this CMS and occasionaly help me.

Cheers, Batiste

2008-06-20

3 things I would like to see in Django 1.0

I am using Django for more than 2 years now. During several projects I made up I found that some code snippets were missing. These snippets are a common need for me and I think it's maybe the case for others.

CallTag

Just like include, but can pass parameters to it.

This snippet is incredibly useful. Before I was doing things like that:

{% with "64x64" as image_size %}
    {% with 1 as display_birthday %}
        {% include "user.html" %}
    {% endwith %}
{% endwith %}

And for each variable needed the syntax becomes more complicated. With CallTag it would be like that:

{% call include "user.html" with image_size="64x64" display_birthday=1 %}

It's a lot more readable and debuggable. Additionaly, you can "easily" add smart caching using parameters.

Inclusion tags fill not this need because you can't choose the template. Includes with "with" keyword are ugly to read and to write, so IMHO, Django really need this template tag.

New forms output control

The newforms module save lot of time validating and generating automatic forms. But when it comes to control the generated output it another story.

One reason is because the newforms module don't use the Django templating language. IMHO it's the root of the problem. This abstraction is leaky in a bad way.

It will more powerful if the newforms module use templating to render its fields. This way we could override these templates and render the forms the the way we want. Today it's too complicated to take control of the renderer of the final form inputs. Costum Fields and Widgets objects could be created at this purpose but it's not a piece of cake.

One solution is to take the form auto rendering output as a scaffold and tweak it. It's not a bad solution but I think it's possible to do better.

Query several models with an Union

As far has I know it's not possible to create a SQL Union request on several models as if there is only one.

The possible solution is to create a SQL View into the database and a Django model that match this view but it feel strange to use a database trick to handle this (it's maybe a good idea after all).

It could be nice if Django ORM provide a way to create a Union QuerySet on several tables. Here is an exemple code of what I imagine:

class Post(models.Model):
    title = models.CharField(blank=False)
    author = models.ForeignKey(User, related_name='user_posts')
    body = models.TextField()
    
class Comment(models.Model):
    title = models.CharField(blank=False)
    author = models.ForeignKey(User, related_name='user_comments')
    body = models.TextField()
    
class Product(models.Model):
    name = models.CharField(blank=False)
    owner = models.ForeignKey(User, related_name='user_products')
    description = models.TextField()
    
class Content(models.Union):
    class Meta:
        union_models = (Post, Comment, Product, )
        union_fields = ('title', 'author', 'body' 'content_type',)
        union_fields_rename = ('name as title', 'owner as author',)
    
contents = Content.objects.filter(author_id=1)
2008-05-18

jQuery: Drag & Drop and Resize in an event delegation fashion

I don't see any drag & drop jQuery plugin that works in a event delegation way so I put some code together and it works pretty well. Event delegation is good for 2 things:

  1. Perfomance : Drag & Drop web app are to often too slow. Event delegation could help.
  2. Simplicity : It's painful to handle events for each elements you have and add during the life of the page.

If you plan to build a complex online webapp, you should take a look on event delegation technics. Hope it will help someone.

Example : Drag & Drop and Resize example.

Download the plug in.

Credits

Inspired by EasyDrag jQuery plug in.