Building a PHP Profiler Part 2: PHP Memory Usage

Last week’s post included a simple PHP profiler class. This week we will expand on it and add in features to report on PHP memory usage within your script. We also add in some percentage reporting to make it easier to see where the most time and memory is being used.

<?php
class Profiler{
o
oconst PROFILER_START_GLOBAL = 'profiler_start';
o
ostatic protected $all_profilers;
o
ostatic protected $current_index = 0;
oprotected $index;
oprotected $name;
oprotected $start_time;
oprotected $start_memory;
oprotected $checkpoints;
o
ofunction __construct(){
o++self::$current_index;
o$this->set_index(self::$current_index);
oself::$all_profilers[self::$current_index] = &$this;
o}
o
ostatic function init_profiler($name='Main'){
o$prof = new Profiler();
o$prof->set_start_time(microtime(true));
o$prof->set_start_memory(memory_get_usage());
oif($prof->get_index() == 0 && self::global_start_exists()){//check for the start time global since this is the main profiler
o$prof->set_global_start();
o$prof->set_start_memory(0);
o}
o$prof->set_name($name);
o
oreturn $prof;
o}
o
ofunction get_index(){return $this->index;}
oprivate function set_index($index){$this->index = $index;}
o
oprivate function get_start_time(){
oreturn $this->start_time;
o}
oprivate function set_start_time($time){$this->start_time = $time;}
o
oprivate function get_start_memory(){
oreturn $this->start_memory;
o}
oprivate function set_start_memory($memory){$this->start_memory = $memory;}
o
ostatic private function global_start_exists(){
oreturn ($GLOBALS[self::PROFILER_START_GLOBAL]);
o}
ofunction set_global_start(){
oif(self::global_start_exists()){
o$this->set_start_time($GLOBALS[self::PROFILER_START_GLOBAL]);
o}
o}
o
ofunction get_name(){return $this->name;}
ofunction set_name($name){$this->name = $name;}
o
ofunction get_elapsed_time(){
o$current_time = microtime(true);
o$elapsed = $current_time - $this->get_start_time();
oreturn $elapsed;
o}
o
ofunction get_elapsed_memory(){
o$current_mem = memory_get_usage();
o$elapsed = $current_mem - $this->get_start_memory();
oreturn $elapsed;
o}
o
ostatic function add_profiler_checkpoint($checkpoint_name,$index=1){
o$profiler = self::$all_profilers[$index];
oif(!$profiler){
o$profiler = Profiler::init_profiler($checkpoint_name.'_profiler');
o}
o$profiler->add_checkpoint($checkpoint_name);
o}
o
ofunction add_checkpoint($checkpoint_name){
o$checkpoint = array();
o$checkpoint['name'] = $checkpoint_name;
o$checkpoint['time'] = $this->get_elapsed_time();
o$checkpoint['memory'] = $this->get_elapsed_memory();
o$this->checkpoints[] = $checkpoint;
o}
ofunction get_checkpoints(){return $this->checkpoints;}
o
ofunction get_result($newline='<br>'){
o$total_time = $this->get_elapsed_time();
o$total_memory = $this->get_elapsed_memory();
o$profiler_text = '';
o$profiler_text .= 'Profiler '.$this->get_index().': '.$this->get_name().$newline;
oif(count($this->get_checkpoints()) > 0){
o$profiler_text .= 'Checkpoints: '.count($this->get_checkpoints()).$newline;
o$profiler_text .= '--Checkpoint Name-- --Elapsed Time-- --time%-- --memory-- --memory%--'.$newline;
o$prev_checkpoint = array('time'=>0);
oforeach($this->get_checkpoints() as $checkpoint){
o$timepercent = self::get_percent($prev_checkpoint['time'],$checkpoint['time'],$total_time);
o$mempercent = self::get_percent($prev_checkpoint['memory'],$checkpoint['memory'],$total_memory);
o$profiler_text .= $checkpoint['name'];
o$profiler_text .= ' ';
o$profiler_text .= round($checkpoint['time'],4);
o$profiler_text .= ' ';
o$profiler_text .= round($timepercent,2);
o$profiler_text .= ' ';
o$profiler_text .= round($checkpoint['memory'],4);
o$profiler_text .= ' ';
o$profiler_text .= round($mempercent,2);
o$profiler_text .= $newline;
o$prev_checkpoint = $checkpoint;
o}
o}
o$final_percent_time = self::get_percent($prev_checkpoint['time'],$total_time,$total_time);
o$final_percent_memory = self::get_percent($prev_checkpoint['memory'],$total_memory,$total_memory);
o$profiler_text .= '--Final ';
o$profiler_text .= round($total_time,4);
o$profiler_text .= ' ';
o$profiler_text .= round($final_percent_time,2);
o$profiler_text .= ' ';
o$profiler_text .= round($total_memory,4);
o$profiler_text .= ' ';
o$profiler_text .= round($final_percent_memory,2);
o$profiler_text .= $newline;
o
oreturn $profiler_text;
o}
o
ofunction get_percent($prev_checkpoint_value,$this_checkpoint_value,$total_value){
o$checkpoint_value = $this_checkpoint_value - $prev_checkpoint_value;
o$percent = $checkpoint_value / $total_value;
oreturn $percent*100;
o}
o
ostatic function get_all_results($newline='<br>'){
o$text = 'Profilers'.$newline;
oforeach(self::$all_profilers as $profiler){
o$text .= $profiler->get_result($newline);
o}
o$text .= '----------'.$newline;
oreturn $text;
o}
}

PHP memory usageNotice that the new memory management piece is fairly lightweight. It will work without changing any existing code that uses the old profiler class. On line 24 the memory can be set to start at something other than 0. This is to accommodate any extra profilers you want to run. If, for example, you want to check the performance of a large function using a separate profiler then this will tell you the memory since the start of the profiler. Also take a look at the changes to get_result() including the new percentages for time and memory. Next week we will expand this profiler to report on database load. For usage see last week’s post.

One thought on “Building a PHP Profiler Part 2: PHP Memory Usage

Leave a Reply

Your email address will not be published. Required fields are marked *