Spotting performance leaks in your application
Posted by Felix Geisendörfer, on Jun 18, 2006 - in PHP & CakePHP » Performance, Optimization & Caching
Today I was implementing a new and critical functionality into SpliceIt! that allows plugins to register global hooks like beforeFilter, afterFilter, etc. in order to make their own changes to the AppController (SpliceItAppController), even when they are not beeing requested. That way you can simply drop in a menu plugin, and your layout will automatically have the variable $menu set, even so the user didn't request any action from the menu plugin itself.
Anyway, when I implemented the first version I noticed a good performance loss right away, which I didn't like very much. So I had to figure out which part of my code was responsible for giving my processor such a hard time ; ). In order to do this I wrote a useful little singleton class that allows you to start, pause and resume multiple timers throughout your app in order to see what parts are eating up what performance.
Usage is very simple, after you've included the class with require/vendor/whatever you can use it like this:
-
Performance::startTimer('Sleep Walking');
-
Performance::pauseTimer('Sleep Walking');
-
Performance::resumeTimer('Sleep Walking');
-
-
Performance::debugTimersPercantage();
-
-
// Outputs an array like this:
-
// Array
-
// (
-
// [sleep walking] => 1.99932789803 (52.04%)
-
// [_total] => 3.84202480316 (100%)
-
// )
Want to play around with it yourself? Here is the code:
-
class Performance
-
{
-
var $time_start;
-
-
/**
-
* Returns a singleton instance of the Performance class
-
*
-
* @return unknown
-
*/
-
function &getInstance()
-
{
-
if (!$instance) {
-
$instance[0] = &new Performance;
-
}
-
return $instance[0];
-
}
-
-
/**
-
* In the constructor we check if we already know the time our Application started (by checking for
-
* the variable $TIME_START like cakephp set's it. Or we use the current time as our starttime.
-
*
-
* @return Performance
-
*/
-
function Performance()
-
{
-
global $TIME_START;
-
-
$this->time_start = $TIME_START;
-
else
-
$this->time_start = $this->getMicrotime();
-
}
-
-
/**
-
* Starts a new timer $key. If such a timer has already been started it's going to be reset to 0.
-
*
-
* @param string $key
-
*/
-
function startTimer($key)
-
{
-
$_this =& Performance::getInstance();
-
-
$_this->_timers[$key] = $_this->getMicrotime();
-
}
-
-
/**
-
* Pauses the timer $key. You can resume it using Performance::resumeTimer()
-
*
-
* @param string $key
-
*/
-
function pauseTimer($key)
-
{
-
$_this =& Performance::getInstance();
-
-
}
-
-
/**
-
* Resumes the timer $key if it had been paused before. If not nothing happens, and if such a timer
-
* doesn't exists it get's created via Performance::startTimer() automatically.
-
*
-
* @param string $key
-
*/
-
function resumeTimer($key)
-
{
-
$now = Performance::getMicrotime();
-
$_this =& Performance::getInstance();
-
-
-
if (!$_this->isKeySet($key))
-
return $_this->startTimer($key);
-
-
$timerStart = $_this->_timers[$key];
-
-
}
-
-
/**
-
* Removes the timer $key from the list of timers
-
*
-
* @param unknown_type $key
-
*/
-
function removeTimer($key)
-
{
-
$_this =& Performance::getInstance();
-
-
}
-
-
/**
-
* Get's the current amount of time ellapsed for timer $timer.
-
*
-
* @param string $key
-
* @return float
-
*/
-
function getTimer($key, $now = null)
-
{
-
$now = Performance::getMicrotime();
-
-
$_this =& Performance::getInstance();
-
-
$timerStart = $_this->_timers[$key];
-
-
-
else
-
return $now - $timerStart;
-
}
-
-
/**
-
* Get's a list of all registered timers and their current amount of ellapsed time.
-
*
-
* @return array
-
*/
-
function getTimers()
-
{
-
$now = Performance::getMicrotime();
-
-
$_this =& Performance::getInstance();
-
-
-
foreach ($_this->_timers as $key => $timer)
-
{
-
$timers[$key] = $_this->getTimer($key, $now);
-
}
-
-
$timers['_total'] = $now-$_this->time_start;
-
-
return $timers;
-
}
-
-
/**
-
* Returns the percantage that the timer $key has taken up in time compared
-
* to the total execution time of the script (see the constructor to make sure
-
* this works).
-
*
-
* @param string $key
-
* @param float $now
-
* @param float $timer
-
* @return float
-
*/
-
function getTimerPercantage($key, $now = null, $timer = null)
-
{
-
$now = Performance::getMicrotime();
-
-
$_this =& Performance::getInstance();
-
$timer = $_this->getTimer($key, $now);
-
-
}
-
-
/**
-
* Get's a list of all timers together with the time percantage they have used up.
-
* The total may add up to over 100% if some of the timers have been running at the
-
* same time.
-
*
-
* @return array
-
*/
-
function getTimersPercantage()
-
{
-
$now = Performance::getMicrotime();
-
$_this =& Performance::getInstance();
-
-
$timers = $_this->getTimers();
-
-
foreach ($timers as $key => $timer)
-
{
-
$percantageTimers[$key] = $_this->getTimerPercantage($key, $now, $timer);
-
}
-
-
return $percantageTimers;
-
}
-
-
/**
-
* Checks if the timer $key exists or not.
-
*
-
* @param unknown_type $key
-
* @return unknown
-
*/
-
function isKeySet($key) {
-
$_this =& Performance::getInstance();
-
-
}
-
-
/**
-
* A convenience function for debug(Performance::getTimer($key));
-
*
-
* @param string $key
-
*/
-
function debugTimer($key)
-
{
-
debug(Performance::getTimer($key));
-
}
-
-
/**
-
* A convenience function for debug(Performance::getTimers($key));
-
*
-
* @param string $key
-
*/
-
function debugTimers()
-
{
-
debug(Performance::getTimers());
-
}
-
-
/**
-
* A convenience function for debug(Performance::getTimerPercantage($key));
-
*
-
* @param string $key
-
*/
-
function debugTimerPercantage($key)
-
{
-
debug(Performance::getTimerPercantage($key));
-
}
-
-
/**
-
* A convenience function for debug(Performance::getTimersPercantage($key));
-
*
-
* @param string $key
-
*/
-
function debugTimersPercantage()
-
{
-
debug(Performance::getTimersPercantage());
-
}
-
-
/**
-
* Returns the microtime in seconds as a float. I know php5 / cakephp already have this function,
-
* but I wanted a maximum of reusability for this class.
-
*
-
* @return float
-
*/
-
function getMicrotime()
-
{
-
}
-
}
The license for it is MIT. Another thing I'd like to point out, is the fact that some of the code was inspired/derived from CakePHP's ClassRegistry class which you can find inside cake/libs/class_registry.php.
If you like the class or find a bug, any feedback is welcome ; ).
--Felix Geisendörfer aka the_undefined