Simple profiler

Coding Critique is the place to post source code for peer review by other members of DevNetwork. Any kind of code can be posted. Code posted does not have to be limited to PHP. All members are invited to contribute constructive criticism with the goal of improving the code. Posted code should include some background information about it and what areas you specifically would like help with.

Popular code excerpts may be moved to "Code Snippets" by the moderators.

Moderator: General Moderators

Post Reply
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Simple profiler

Post 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.
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
Post Reply