How to speed up template rendering in Symfony2?

Viewed 13483

I have a symfony2 application that renders about 6000 database entities.

I optimized the ORM (propel) queries and the database requests are now very fast. What remains is the symfony controller and the twig template rendering engine.

Is there a way to speed up rendering (e.g. switching to php templates?) Can I get more detailed profiling information than from the symfony profiler?

Here's an excerpt from the profiler data.

symfony profiler screenshot

Edit: I profiled my code using xDebug and found that generating the objects is very expensive. Creating thousands of PHP objects from database rows in PropelObjectFormater->getAllObjectsFromRow takes most of the time.

profiler_run_excerpt

The image shows the branch that takes up 95% of the computing time, which is ~2.8 seconds. The database retrieval takes ~0.5 seconds, the rendering ~1 second and formatting takes the most time with ~1.5 seconds.

I'm not sure what impact the length of the file containing the PHP class has, but Propel generates a lot of code (my most complex entity base class has almost 10k lines of code) so this might slow down object creation as well.

I think using an array formatter (thus bypassing the object creation step) would be a solution, but that somewhat defeats the purpose of the ORM.

6 Answers

When in production mode, Symfony2's Twig template rendering is actually done using cached templates, meaning that it is a PHP template; a Twig template compiled into pure PHP code. In dev mode, it of course doesn't cache the templates like that, but compiles it each time. Read more about it here, in the docs.

It will take PHP some time to compile a page with that much data, even when the template is just the normal PHP-HTML infusion code that tends to perform fastest. - You may want to look into normal HTTP caching, like Varnish, if this page is something that doesn't have to be rendered from the database each time.

As for the profiling. Something like XDebug should get you more detailed profiling info than what is built into Symfony2.

6K of iterations is a lot of work.

Idea #1:

Can you implement data pagination? That seems highly reasonable...

Idea #2 (thick client):

Implement some JavaScript templating mechanism. Your controller should then return JSON in order for JS to go though it and render it completely.

Even in this situation, relying of browser's speed to do 6K loops fast is lot to ask. You will need to implement pauses in between (like, after every 150th iteration) so the browser doesn't go into non-responsive mode...

It sounds like you are need to use PHP twig extension, which was made for your situation.

From docs:

And from now on, Twig will automatically compile your templates to take advantage of the C extension. Note that this extension does not replace the PHP code but only provides an optimized version of the Twig_Template::getAttribute() method.

I personally didn't get almost any visible impact, when I tried it a year ago for my project. Please share your experience, if you use it.

You ask two questions here:

1) how to accelerate template rendering ?

The profiler tells that queries are casted threw the rendering, I think you forgot a join, it's preferable to execute one big query (maybe cachable) before the rendering, and just use the query results in view layer.

2) How to get more detailled profiling data ?

I can suggest to install xhprof for advenced profiling, you can use this great bundle: https://github.com/jonaswouters/XhprofBundle. Xhprof track php calls and build statistics of time execution and a visual call graph. I use it to detect which process results I have to store in the cache.

I cached the view in the redis.

When rendering the view, Twig will get the data by query, waiting the query takes some time. So the fast way to display that is never query. In my project, when some place isn't change so ofen, I cached the html code of that part, by use the renderView function in the controller.

Building on @jperovic answer:

Like he said you can create an API to throw out the JSON data, and put the work on the client side. In addition maybe you don't need to display all 6000 entities at once? So lazy loading the additional data via ajax calls could help.

On the database side, cache the results and break the cache every so often to see if there is any new data. Or if the data needs to be up to date at all times: Whatever piece of the application updates the data will also send a request to tell your API that it should break the cache.

Related