Page 1 of 1

Simple profiler

Posted: Fri Apr 30, 2010 7:16 am
by s.dot
Just wrote a little script for fun to profile the performance of multiple events within a single script. It's pretty cool me thinks and extremely simple.

The Script:

Code: Select all

<?php

/**
 * Simple profiler class for checking speed performance of multiple events 
 * in a single script or checking for possible bottlenecks.
 * 
 * It can output results as an array or as an html table for visual processing
 *
 * @author Scott Martin <sjmdot[at]gmail[dot]com>
 * @version 1.0.0
 * @version-history
 *     1.0.0 - Initial release
 * @date April 30th, 2010
 */
 
class profiler
{
	//holds events
	private $events = array();
	
	//holds number of events to stop repetitive calls to count()
	private $events_num = 0;
	
	//the number of decimal places to round to
	private $round_to = 3;
	
	/**
	 * Starts the timing of an event
	 * 
	 * @access public
	 * [@param string $event] - the name of the event being timed,
	 *    defaults to empty string
	 * @return null
	 */
	public function start_event($event='')
	{
		//stop previous event if it hasn't been stopped
		$this->stop_event();
		
		//add to events array
		$this->events[] = array(
			'name' => (empty($event) === true) ? 'Un-named Event' : $event,
			'start' => microtime(true)
		);
		
		//increase event count
		$this->events_num += 1;
	}
	
	/**
	 * Stops the currently running event, or, if called after another event is
	 * is added, stops the previous event.  Not required to call, but improves
	 * code readability
	 *
	 * @access public
	 * @param void
	 * @return null;
	 */
	public function stop_event()
	{
		if ($this->events_num)
		{
			$event_index = $this->events_num - 1;
			
			if (!isset($this->events[$event_index]['end']))
			{
				$this->events[$event_index]['end'] = microtime(true);
			}
		}
	}
	
	/**
	 * Gathers the results of the events by calculating the duration of 
	 * each event and then adding a totals section. Can be returned as 
	 * an array or as an html table if $html_table is set to true
	 *
	 * @access public
	 * [@param boolean $html_table] - whether or not to return the result 
	 *     as an html table.  defaults to false
	 * @return mixed - array or string depending on $html_table param
	 */
	public function profile($html_table = false)
	{
		//stop previous event if it hasn't been stopped
		$this->stop_event();
		
		//return array
		$ret = array();
		
		//loop and calculate
		foreach ($this->events AS $event)
		{
			$event['duration'] = round($event['end'] - $event['start'], $this->round_to);
			$ret[] = $event;
		}
		
		//totals
		$ret[] = !empty($ret) ? 
			array(
				'name' => 'Profiler Totals (' . $this->events_num . ' Events)',
				'start' => $ret[0]['start'],
				'end' => $ret[$this->events_num-1]['end'],
				'duration' => round($ret[$this->events_num-1]['end'] - $ret[0]['start'], $this->round_to)
			) :
			array(
				'name' => 'Profiler Totals',
				'start' => null,
				'end' => null,
				'duration' => null
			);
		
		return $html_table ? $this->generate_html_table($ret) : $ret;
	}
	
	/**
	 * Generates and returns an HTML table from the results
	 *
	 * @access private
	 * @param $results
	 * @return string
	 */
	private function generate_html_table($results)
	{
		$totals = array_pop($results);
		$ret = '<table cellspacing="0" cellpadding="2" border="1">
			<tr>
				<td bgcolor="#FFFFFF"><strong>Event</strong></td>
				<td bgcolor="#FFFFFF"><strong>Duration</strong></td>
			</tr>';

		foreach ($results AS $k => $v)
		{
			$ret .= '<tr>
				<td bgcolor="#FFFFFF">' . $v['name'] . '</td>
				<td bgcolor="#FFFFFF">' . $v['duration'] . 's</td>
			</tr>';
		}
		
		$ret .= '<tr>
			<td bgcolor="#E6E6E6"><strong>' . $totals['name'] . '</strong></td>
			<td bgcolor="#E6E6E6">' . $totals['duration'] . 's</td>
		</tr>
		</table>';
		
		return $ret;
	}
	
	/**
	 * Sets the number of decimal places to round to for duration
	 *
	 * @access public
	 * @param integer $roundto
	 * @return null
	 */
	public function set_round_to($roundto)
	{
		$this->round_to = (int) $roundto;
	}
}
The Test:

Code: Select all

$profiler = new profiler();

//do something so we can profile
//time getting remote contents
$profiler->start_event('Grabbing google content');
file_get_contents('http://www.google.com');
$profiler->stop_event();

//do something again so we can profile
//time looping a dumb math equation
$profiler->start_event('Looping a dumb math equation');
for ($i=0; $i<1000; $i++)
{
	sqrt(mt_rand(1, 99999)) * rand(1, 2);
}
$profiler->stop_event();

//do something again
//like sleep?
$profiler->start_event('Sleeping a little bit');
sleep(3);
$profiler->stop_event();

$profiler->start_event('Adding hi');
$hi = 'hi';
$profiler->stop_event();

//get an array of results
echo '<pre>';
print_r($profiler->profile());
echo '</pre>';

//get an html table of results
echo $profiler->profile(true);
The results as an array:

Code: Select all

Array
(
    [0] => Array
        (
            [name] => Grabbing google content
            [start] => 1272629527.51
            [end] => 1272629527.71
            [duration] => 0.197
        )

    [1] => Array
        (
            [name] => Looping a dumb math equation
            [start] => 1272629527.71
            [end] => 1272629527.71
            [duration] => 0.002
        )

    [2] => Array
        (
            [name] => Sleeping a little bit
            [start] => 1272629527.71
            [end] => 1272629530.71
            [duration] => 2.993
        )

    [3] => Array
        (
            [name] => Adding hi
            [start] => 1272629530.71
            [end] => 1272629530.71
            [duration] => 0
        )

    [4] => Array
        (
            [name] => Profiler Totals (4 Events)
            [start] => 1272629527.51
            [end] => 1272629530.71
            [duration] => 3.193
        )

)
The results as an html table:
Image

This is the kind of stuff that is fun to write! I found it interesting to note that sleep(3) took 2.993 seconds. If anything, my profiling script would add some milliseconds of time to that.. so hmm.