Hi again,

That's the last one of my improvements ideas for the moment, but it's a 
rather old one actually. And I have been using for a few months the changes 
I am about to propose.
First, I would like to thank Dan for the addition of this great debugging 
feature. Then, I would like to discuss even further improvements, 
especially ones bringing knowledge of memory consumption as well as a 
detail version of the saved information, ,like the arguments of the called 
functions.

Most of the changes are in engine.php, where the core of the StopWatch 
feature resides.

First, I propose to introduce new global variables:
- $BOLTstopWatchStats (instead of $BOLTstopWatchTime) that would hold time 
and memory consumption, as well as the number of times each 
function/command is called/run.
- $BOLTstopWatchMaxTime and $BOLTstopWatchMaxMemory to know what is the 
longest process or the heaviest (in terms of memory) one. Really useful to 
spot problems whitout having to go through the whole output.
- $BOLTscriptExecutionStart to set a starting point in the measures, 
independent from where the first call to BOLTstopWatch lies on the scripts.

In order to really know how long took a function/command/whatever, we need 
a starting date, and I propose to replace
if (isset($BOLTstopWatch)) BOLTstopWatch('Initialization begun');  // 
Stopwatch is used to benchmark performance
by
$BOLTscriptExecutionStart = microtime(true);
if (isset($BOLTstopWatch)) BOLTstopWatch('Initialization begun');  // 
Stopwatch is used to benchmark performance 

We will also need a time and memory measurement function in order to avoid 
code redundancy and ad some modularity, and here comes BOLTstopWatchMeasure:
function BOLTstopWatchMeasure() {
 return array(microtime(true),memory_get_usage());
 }

With such a function, we can now easily replace
 global $BOLTstopWatch, $BOLTstopWatchMsg; 
 if ($args['stopwatch'] == 'true' || inlist($BOLTstopWatch, 'all,commands')) 
{
 global $BOLTswi; // stop watch index...
 $i = $i + 1;
 $m = microtime();
 $BOLTswi[$i] = substr($m, -10) . substr($m, 2, 4);
 }
by
 global $BOLTstopWatch, $BOLTstopWatchStats; 
 if ($args['stopwatch'] == 'true' || inlist($BOLTstopWatch, 'all,commands')) 
{
 $commandStartStats = BOLTstopWatchMeasure();
 } 

and
if ($args['stopwatch'] == 'true' || inlist($BOLTstopWatch, 'all,commands')) 
{
 $m = microtime();
 $t2 = substr($m, -10) . substr($m, 2, 4);
 $t = ($t2 - $BOLTswi[$i]) / 10000;
 $BOLTstopWatchMsg = $BOLTstopWatchMsg . "<br 
/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;command " . substr($do, 5) . " took $t 
seconds";
 }
by
 if ($args['stopwatch'] == 'true' || inlist($BOLTstopWatch, 'all,commands')) 
{
 $commandEndStats = BOLTstopWatchMeasure();
 $BOLTstopWatchStats[count($BOLTstopWatchStats)-1][3][] = array('command '.
substr($do, 5),($commandEndStats[0]-$commandStartStats[0]),$commandEndStats[
1]-$commandStartStats[1],print_r($args,true));
 }

This code comes from BOLTcommand, but the changes are similar in BOLTfunc, 
and I even took the liberty on my website to use it also in BOLTiftrue, as 
well as in a homemade plugin.


With all these changes, BOLTstopWatch would become:
function BOLTstopWatch($text) {
## A SPECIAL FUNCTION TO HELP BENCHMARK PROCESSES ON BOLTWIRE SITES. TO 
USE, SET $BOLTstopWatch="all", $BOLTstopWatch="functions" OR 
$BOLTstopWatch="commands" IN INDEX.PHP BEFORE CALLING ENGINE 
 global $BOLTstopWatch, $BOLTstopWatchStats, $BOLTstopWatchMaxTime, 
$BOLTstopWatchMaxMemory, $BOLTscriptExecutionStart;
 if (!isset($BOLTstopWatch) || $BOLTstopWatch === false) return;
 $stats = BOLTstopWatchMeasure();
 if (count($BOLTstopWatchStats)==0) {//first time
 $BOLTstopWatchStats[] = array("Memory already in use", $stats[0], $stats[1
]);
 }
 $BOLTstopWatchMaxTime = max($BOLTstopWatchMaxTime,($stats[0]-
$BOLTstopWatchStats[count($BOLTstopWatchStats)-1][1]));
 $BOLTstopWatchMaxMemory = max($BOLTstopWatchMaxMemory,($stats[1]-
$BOLTstopWatchStats[count($BOLTstopWatchStats)-1][2]));
 $BOLTstopWatchStats[] = array($text, $stats[0], $stats[1]);
 }

As you can see, no text processing/assembling here, as this can be done at 
display time. We would also like to display in the text the final values of 
some variables, like the total number of calls to a given function, or the 
duration of the longest process.

One other thing I added is the formatting of output data, and so here 
come formatDecimal and formatBytes:
function formatDecimal($number, $length = 5) {
 return number_format($number, $length, '.', '\'');
 } 


function formatBytes($bytes, $precision = 2) { 
    $units = array('B ', 'KB', 'MB', 'GB', 'TB'); 
    $bytes = max($bytes, 0); 
    $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); 
    $pow = min($pow, count($units) - 1); 
    // Uncomment one of the following alternatives
    $bytes /= pow(1024, $pow);
    // $bytes /= (1 << (10 * $pow)); 
 $res = round($bytes, $precision);
 if(strpos($res,'.')) $precision++;
    return formatDecimal($res, $precision) . ' ' . $units[$pow]; 
 }


And thus,
if (isset($BOLTstopWatch)) {
 BOLTstopWatch('Page construction complete');
 if (inlist('admin', $BOLTgroups)) $out = str_replace("<body>", "<body><div 
class='stopwatch'>$BOLTstopWatchMsg</div>", $out);
 }

becomes
if (isset($BOLTstopWatch)) {
 BOLTstopWatch('Page construction complete');
 $globalstats = BOLTstopWatchDisplayResult(microtime(true), memory_get_usage
(), memory_get_peak_usage());
 $stopWatchText = '<div class="stopwatch"><strong>StopWatch:</strong><br />'
.$BOLTstopWatchStats[0][0].': '.formatBytes($BOLTstopWatchStats[0][2],3).'<br 
/>'.$globalstats.'<br /><ul>'.$BOLTstopWatchMsg.'</ul>';
 if(strpos($BOLTstopWatch,'detail') !== false) $stopWatchText .= '<br /><br 
/>Details:<br/><ul>'.$BOLTstopWatchMsgDetail.'</ul>';
 $stopWatchText .= '</div>';
 if (inlist('admin', $BOLTgroups)) {
 $out = str_replace('<body>', '<body>'.$stopWatchText, $out);
 }
 }

where BOLTstopWatchDisplayResult is the following function:
function BOLTstopWatchDisplayResult($t, $m, $mmax) {
 global $BOLTstopWatchStats, $BOLTstopWatchMaxTime, $BOLTstopWatchMaxMemory, 
$BOLTstopWatchMsg, $BOLTstopWatchMsgDetail;
 $globalstats = 'You are using PHP '.PHP_VERSION.'<br />Total duration: '.
formatDecimal($t-$BOLTscriptExecutionStart),6).' seconds<br />Memory usage: 
'.formatBytes($m,3).' / '.formatBytes($mmax,3).'<br />';
 $globalstats .= 'The longest process took '.formatDecimal(
$BOLTstopWatchMaxTime,6).' seconds.<br />';
 $globalstats .= 'The most greedy process used '.formatBytes(
$BOLTstopWatchMaxMemory,3).' of memory.<br />';
 
 $c = count($BOLTstopWatchStats);
 for($i = 1; $i < $c; $i++) {
 $stats = $BOLTstopWatchStats[$i];
 $prevStats = $BOLTstopWatchStats[$i-1];
 if($BOLTstopWatchMaxTime == $stats[1]) $msg = '<li class="maxtime">';
 else if($BOLTstopWatchMaxMemory == $stats[2]) $msg = '<li 
class="maxmemory">';
 else $msg = '<li>';
 $msg .= 'Duration: '.formatDecimal(($stats[1]-$prevStats[1]),6).' seconds 
- Memory: '.formatBytes($stats[2]-$prevStats[2],3).' - <span 
style="font-size:larger;">'.$stats[0].'</span>';
 $detailMsg = $msg.'<ul>';
 foreach($stats[3] as $execStats) {
 $execs[$execStats[0]]++;
 $detailMsg .= '<li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'.$execStats[0].' took '.
formatDecimal($execStats[1],6).' seconds and used '.formatBytes($execStats[2
],3).' of memory; arguments were:'."\n".'<br 
/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'.$execStats[3
].'</li>';
 }
 $detailMsg .= '</ul>';
 foreach($execs as $f=>$nb) {
 $msg .='<br />&nbsp;&nbsp;'.$f.' was called '.$nb.' time(s)';
 }
 $BOLTstopWatchMsg .= msg.'</li>';
 $BOLTstopWatchMsgDetail .= $detailMsg.'</li>';
 }
 return $globalstats;
 }


For you information, I also added in engine.php BOLTstopWatch('Startup 
process begun') and BOLTstopWatch('Startup process 
complete'), BOLTstopWatch('Config loading begun'), BOLTstopWatch('Config 
loading complete'), BOLTstopWatch('Loading plugins 
begun'), BOLTstopWatch('Loading plugins complete'), BOLTstopWatch('Pre 
process begun'), BOLTstopWatch('Pre process 
complete'), BOLTstopWatch('Choosing skin begun'), BOLTstopWatch('Choosing 
skin complete'), BOLTstopWatch('Making page begun') 
and BOLTstopWatch('Making page complete'), but most of them are probably 
not needed unless in a heavily debugging situation.


And going further, I wanted not to consider identical a function call of 
different types. Like [(search, <(search, and {(search... This is helpful 
in the detailed version of what I am providing.
Hence a few more changes in BOLTMfunc and markups.php:
MarkUp('func', 'math', '/\{ ([\-\+\*\/\.0-9\(\) ]+) \}/e', "BOLTMfunc('math 
$1','{')");  // { 1+1 }
MarkUp('func', 'vars', '/\{\((.*?)\)\}/e', "BOLTMfunc('$1','{')");  // 
{(func params)}
MarkUp('func', 'func', '/\[\((.*?)\)\]/e', "BOLTMfunc('$1','[')");  // 
[(func params)]
MarkUp('form', 'func', '/(&lt;|<)\((.*?)\)(&gt;|>)/e', 
"BOLTMfunc('$2','&lt;')");  // <(func params)>

function BOLTMfunc($params,$type='') {
 global $BOLTzone;
 $params = str_replace('&amp;&amp;', '&&', BOLTstripSlashes($params));
 $function = $params;
 if (strpos($params, " ") !== false) {
 $function = substr($params, 0, strpos($params, " "));
 $params = substr($params, strlen($function));
 $args = BOLTargs($params);
 }
 else $args = Array();
 if ($args['if'] != '' ) {
 if (inlist($function, 'search,list')) {
 deprecate('if2when');
 $args['when'] = $args['if'];
 unset($args['if']);
 }
 else {
 if (BOLTiftrue($args['if'], 'true') != 'true') return;  // delete lines 
254-259,261 on 5.xx 
 }
 }
 if ($args['msg'] == '') $args['msg'] = BOLTconfig('funcMessages', false);
 $args['functype'] = $type;//used for debugging purposes in BOLTfunc
 return BOLTfunc($function, $args, strtolower($BOLTzone));
 }
and the following change in BOLTfunc:
if(isset($args['functype'])) $function = $args['functype'].$function;
 $BOLTstopWatchStats[count($BOLTstopWatchStats)-1][3][] = array("function 
$function",($functionEndStats[0]-$functionStartStats[0]),$functionEndStats[1
]-$functionStartStats[1],print_r($args,true));


And... oof... that's all. I wanted to put all of these down before version 
5 comes. I am not pressuring or else, these are more sharing thoughts and 
listing my changes somewhere it could also help someoneelse, than asking 
for features.

Cheers,
Tiffany

-- 
You received this message because you are subscribed to the Google Groups 
"BoltWire" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/boltwire.
For more options, visit https://groups.google.com/d/optout.

Reply via email to