< February 2007 >
     1 2 3
4 5 6 7 8 910
Sat, 03 Feb 2007:

After nearly a year of messing around with php extensions, I've finally sat down and written a full extension from scratch. I've used all the skeletons and ext_skel scripts, in the proper way to end up with a half-decent extension. It took me around 4 hours from an empty directory to end up with an extension which basically did what I wanted.

hidef: The define() call in php is slow. Previously the workaround to define a large chunk of constants was to use apc_load_constants, which pulled out stuff from the cache, but still had to define all constants for every one of the requests. Even beyond that the value replacement is at runtime, nearly as expensive as a $global['X']. A quick look with vld indicates the problem very clearly.

define('ANSWER', 42);
echo "The answer is ".ANSWER;

	line     #  op           operands
   2     0  SEND_VAL        'ANSWER'
         1  SEND_VAL        42
         2  DO_FCALL        'define', 0
   3     3  FETCH_CONSTANT  ~1, 'ANSWER'
         4  CONCAT          ~2, 'The+answer+is+', ~1
         5  ECHO            ~2

For a lot of code with a lot of defines(), this is a hell of a lot of CPU wasted just putting data in & reading it out, where a substitution would be much better. But first things first, I got a basic extension which would parse a .ini file and define the constant with some magic flags - this is what you'd put into the ini file.

float PIE = 3.14159;
int ANSWER = 42;

The extension reads this once when apache starts up and puts into the php's constants section. The constant is pushed in with the CONST_PERSISTENT flag which means that the constant lives across requests. Recently, Dmitry had put in a new bit into this mix - CONST_CT_SUBST which marks constants as canditates for compile time substitution.

After adding compile-time substitution into the extension code, the code generator replaces constants as & when it runs into them. And here's what the bytecode looks like.

echo "The answer is ".ANSWER;

line     #  op      operands
   2     0  CONCAT  ~0, 'The+answer+is+', 42
         1  ECHO    ~0

You don't need to be a genius to figure out which one would be faster. But the other gopal had done some benchmarks which didn't seem to show enough difference between constants and literals. So, I wrote a quick & dirty benchmark with 320 defines and adding them all up in the next line. Here is the before and after numbers.

Before After
380.785 fetches/sec 930.783 fetches/sec
14.2647 mean msecs/first-response 6.30279 mean msecs/first-response

But the true significance of these few hundred lines of code fades a bit when you pull in APC into the mix. With APC enabled I was still expecting a significant difference in performance and here it is.

Before After
976.29 fetches/sec 1519.38 fetches/sec
4.95603 mean msecs/first-response 3.15688 mean msecs/first-response

The numbers are seriously biased, because for most code the major bottleneck is their DB and therefore I/O bound. But if this small bit of code helps shave off a few microseconds of CPU time, for a few hours of my hacking time, it is pretty good when you consider the scale factor.

So, without further ado - here's hidef 0.0.1 - should build fine for both php5 and php4. And if you feel the urge to fix something in there or write documentation, go for it ! :)

If you don't know what procrastination is just look up the definition tomorrow.

posted at: 02:45 | path: /php | permalink | Tags: , ,