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.
<?php 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.
[hidef] 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.
<?php 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.