Posted on Saturday 2 April 2005
Update: download the latest AMFPHP version here.
Yet another update to the beta of AMFPHP 1.0 which now includes a very significant speed boost which is especially apparent in large resultsets, webservices and other complez return types. For a 2-dimensional resultset with about 2000 entries, the speed increase is about 75%. This is on top of the previous AMFPHP speed boost update which was around 50% for a resultset of that size. You do the math. Get it here.
So what happened? After hearing Rasmus’ (the inventor of PHP) talk at the PHPQuebec conference, I felt inspired for some hacking around AMFPHP. Now the bottleneck in AMFPHP is the serializer. Rasmus was talking about how they would create C extensions to PHP at Yahoo! for some of the more mission-critical stuff, and a serializer is certainly that kind of thing that would benefit from some low-level optimization.
Now before I start writing C for the serializer, I thought I would profile the app better to see what kind of a speed boost I can expect. Rasmus mentioned ADP and XDebug which are available on PECL. I settled on XDebug as it has precompiled dlls for Win32. Installation was mostly painless. It creates debug files in a directory you specify. These files are a bit difficult to decrypt though. The final piece of the puzzle is WinCacheGrind, which analyses the debug files using a nice interface.
I can’t believe I spent so much time developing PHP without xDebug! It has to be the most useful PHP extension I’ve seen. You can see a trace of all of your function calls with the time they take. Bottlenecks are identified in a matter of seconds.
Well a couple of things emerged from the analysis. For one thing the action and filter chaining wasn’t working like it should have. You see, a class which filtered an AMF message would send it to the next in the chain and the next would call the third and so on. The problem is that these function calls bubbled up and and when the last filter was being executed the very first filter was still waiting for it to be finished before returning. There’s quite a lot of overhead associated with that.
Well after that tweak on a large resultset AMFPHP was spending 90% of its time in the serializer, as expected. Now the serializer had the same bubbling problem of the filters, so I started with tightening that. Now I’ve always heard local vars are a lot faster and I can now confirm this is true! By moving the buffer calls from a helper class to the main serializer, I saved a huge amount of time.
I also noticed that subclassing abstract classes introduces a lot of overhead as well, as much as 10 ms per such class. So I got rid of those as well. Finally I got rid of the old __describeService subclassing eval’d code mess (don’t ask) and shaved off some more milliseconds there.
My test case of a resultset with 4 columns and 500 rows went from around 1.6s to around 400ms. Less than half a second for sending back a huge amount of data! It only ends up to about 20k of AMF, but the equivalent in HTML tables is probably at least three times as much. Writing the thing in C seems pretty pointless now.
Anyways, pick up the xDebug extension, I swear you’ll see the light!


