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
/> 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> '.$execStats[0].' took '.
formatDecimal($execStats[1],6).' seconds and used '.formatBytes($execStats[2
],3).' of memory; arguments were:'."\n".'<br
/> '.$execStats[3
].'</li>';
}
$detailMsg .= '</ul>';
foreach($execs as $f=>$nb) {
$msg .='<br /> '.$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', '/(<|<)\((.*?)\)(>|>)/e',
"BOLTMfunc('$2','<')"); // <(func params)>
function BOLTMfunc($params,$type='') {
global $BOLTzone;
$params = str_replace('&&', '&&', 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.