Published by Fabian on 12 Mar 2008 at 11:45 am
Another small symfony for a fast response
François describes in his blog post A small symfony for a fast response how to create a new light-weight front controller that directly serves a component.
When I developed a chat system with symfony I had the goal to highly optimize it, however I noticed that it does not make sense to hack around in symfony. I was able to shave about 10ms on my dev machine off the request time but that was quite ugly. And as database queries take up a multitude of this I left this idea alone.
But François brought me back on track. My scenario is slightly different. I have quite a lot of executeXYZ() methods in my chat action, which are quite tightly interwoven. The action this article is about is the one that retrieves all the new lines of chat the user sees. I did optimize this a lot, it takes benefit of APC cache and returns its result as application/json resopnse, with skipping the view rendering.
There a multiple reasons why I had problems with making this a component.
So I tried a different track. I scanned around the symfony code that invokes actions and came up with this action invoking front controller:
define('SF_ROOT_DIR', realpath(dirname(__FILE__).'/..')); define('SF_APP', 'frontend'); define('SF_ENVIRONMENT', 'prod'); define('SF_DEBUG', false); require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR. SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php'); $context = sfContext::getInstance(); $action = $context->getController()->getAction('chat','get'); $action->initialize($context); $action->executeGet(); $context->getResponse()->sendHttpHeaders(); $context->getResponse()->sendContent();
The good thing is that this does not require refactoring, which at this late stage of the project is quite error prone. Also the action code still can be used as it is. So what was left for me to do is to analyze if there is measurable benefit.
The development test
So I fired up the chat application on my development machine and used firebug to watch the response times. Interestingly there was lots of deviation (but no statistics excursion here). But I found one value occurring most of the time (about 80% of the requests) which seems to be a good baseline for comparison.
- Through normal symfony front controller: 268 ms
- Through development front controller: 375 ms
- Through single action controller: 234ms
These numbers show quite interesting facts:
- Symfony is already highly optimized / my optimization is not good enough. just 30ms gained.
- The development front controller is heavy!
- My optimization is still worth it
I chose to utilize the 30ms gained. its near to nothing, but not neglectable. Our chat usually has during daytime about 30 users online. Each of them is requesting every 2 seconds new lines (yes I am aware of Comet/AJAX Push technology. But it does not pay off in PHP according to my tests). This makes 15 requests per second, which ideally would mean maximum 66ms per request. Seeing this together with the 10% gain this brings at virtually no cost, it would be a good idea to see this on the live system.
The live test
I hoped to see also a 10% gain on the live system, but as this is optimized even more on apache, mysql and php level I was not so sure if the 2hour investment into the lightweight controller would pay off.
The system as running at the time of test with a load of 1.17 and my pc needs about 20ms to reach it.
Naturally I get more fluctuation in the firebug timings, but surprisingly I was able to identify two timings which again made up about 80% of the total results.
- Running through the normal symfony front controller: 234ms and 250ms
- Running through the single action front controller: 141ms and 150ms
As this shall not be a benchmark, I am quite happy with the results, even when they are more unstable. In fact that instability of the timing results, are notable. The timings through the single action controller are much more stable. while on the the normal script i have much more variation and even single occurrences of excessive timings.
When browsing through the symfony code this is logical, as more file operations are involved. And file operations are most likely to create more inaccurate timings under load.
And surprisingly the results on the live system are about 30% better than on my machine. I did not expect that.
So if you have a heavy ajax action that you would like to leave as it its, but search a way to execute it faster. Have a look at François tip and my variation, and choose what suits you best. And then try it of course. Do not trust anybody on performance tips until you tried them in your scenario
Bert-Jan on 13 Mar 2008 at 6:48 pm #
Another very simple step that can make a lot of difference is to disable apc.stat in your php.ini. When set to off, apc always serves code from cache and never checks the harddrive for updates, unless it’s cache is cleared. Of course, every time to modify the code you’ll need to clear it’s cache, but that’s easy.
Cruz on 19 Apr 2010 at 1:32 am #
I think that for getting a better analysis in the timing measurement, you could make a stress-test with a tool, or a script custom-made.