Benchmarking and Timing PHP Class
Introducing a simple, yet deceivingly useful, micro benchmarking / timer class called Bench.
I’ve found myself on more than a few occasion using this familiar block of code in order to quickly narrow down a bottleneck or test a specific section of code.
This is a crude but effective way of finding out how long it takes to get from line A to line B. On the down-side, this technique is annoying to implement, only measures time between two points, and makes code look very cluttered — so I started developing Bench.
Bench offers more than just clean looking code or timing between two lines. Its goal is to be more powerful and reusable than simple Microtime Arithmetic while trying avoid stepping on anyone’s toes (*cough* *cough* Xdebug, see below).
Start and Stop
Bench can perform the same operation as the example above but in a much cleaner manner.
// [Some Code To Test]
echo Bench::stop() . ' Seconds.';
“Marks” are simply points in code that one would like tracked by Bench. They are easy to use, quick to implement, and very useful when tracking multiple parts of a script/request.
// [Application Bootstrap]
// [Database Connection Opened]
// [Data Processing + Manipulation]
// [HTML Creation]
Each call to mark() creates a Mark Array that contains the id, microtime of when it occurred, seconds since start, and seconds since the previously called mark.
print_r( Bench::getMarkById( 'database' ) );
[id] => database
[microtime] => 1287969552.88
[since_start] => 1.10582304001
[since_last_mark] => 0.171210050583
When calling Bench::mark(…) it returns ['since_last_mark'] as it tends to be the most sought after piece of information when working with marks. The first call to Bench::mark(…) will return ['since_start'] as — at that point in time — there are no marks in Bench to compare the call with.
I’ve also built in a simple statistics method [ Bench::getStats() ] that returns an array with information about the request up until the point it was called.
// The average time between marks (in seconds)
[mark_average] => 0.346896330516
// The longest mark
[mark_longest] => Array (
[id] => database
[microtime] => 1288045989.62
[since_start] => 1.02174592018
[since_last_mark] => 0.831446886063
// The shortest mark
[mark_shortest] => Array (
[id] => processing
[microtime] => 1288045989.64
[since_start] => 1.04068899155
[since_last_mark] => 0.0189430713654
// Start microtime
[start] => 1288045988.6
// Stop microtime (if called)
// Time elapsed (in seconds)
[elapsed] => 1.0407409668
You may have noticed that ['stop'] is NULL but ['elapsed'] is still populated. That’s because I did not call Bench::stop() in the above example (as it is not required). Bench is meant to be snappy and intuitive; Thus, ['elapsed'] contains the time elapsed between the Bench::start() and Bench::getStats() calls.
I’ve documented many more examples, tips, and tricks over at the Bench Wiki.
Umm… What About Xdebug?!
This is probably one of the first things you thought of when you started reading this post.
Let me first say that I absolutely love Xdebug and have used it on many projects. It’s a huge life saver when you need to profile your entire application or find out what is truly going on within your code. However, when one is just looking to quickly find how long it took to get from line A to B to C then Xdebug can be bit overkill and slower than outputting/logging the result(s) from Bench.
The availability of Xdebug can also become an issue if the app sits on a server where one has limited to no access at all. Having the ability to quickly upload class.Bench.php and start debugging issues only occurring on the server helps to offset this occasional lack of environmental control — making Bench a viable light-weight alternative.
Eric Butera (a fellow developer and friend of mine) articulates my viewpoint quite well:
“Sometimes, you just want a damn number!”
I developed Bench to be a happy middle ground between doing microtime arithmetic and loading up an Xdebug grind file. Whether Bench accomplishes this goal is — for the most part — a matter of personal taste.
Further simplification of Bench can be achieved by writing an autoload function for your development environment which eliminates the need to include the class.Bench.php file. Check out the spl_autoload() documentation for a better understanding of autoloading within PHP as the topic is beyond the scope of this post.
This class and its code is released under the New-BSD License.
- PHP >= 5.0
The source code is available to anyone at http://github.com/veloper/Bench.
If you’ve found an issue, improvement, or bug with this script please Contact Me.