My favorites | Sign in
Project Home Wiki Issues Source
Checkout   Browse   Changes    
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
<?php
// vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
/**
* Container for {@link tgif_benchmark_iterate}
*
* @package tgiframework
* @subpackage debugging
* @copyright 2009 terry chay
* @license GNU Lesser General Public License <http://www.gnu.org/licenses/lgpl.html>
*/
// {{{ tgif_benchmark_iterate
// docs {{{
/**
* Similar to PEAR {@link Benchmark_Iterate}, except it doesn’t violate the
* substitution principle and doesn't have timer start/top overhead
*
* Run the date 100 times and compare it with timezone set date run 1000 times
* <code>
* <?php
* $error_level = error_reporting(0); //mimic production
*
* ini_set('date.timezone',false);
* $b1 = new tgif_benchmark_iterate(true);
* $b1->run(100, 'date', 'c');
* $b1->description = 'date("c") without date.timezone set'; //set after run
* var_dump($b1->summary);
*
* $b2 = new tgif_benchmark_iterate(true);
* ini_set('date.timezone','America/Los_Angeles');
* $b2->run(1000, 'date', 'c');
* $b2->description = 'date("c") with date.timezone set';
*
* echo tgif_benchmark_iterate::format($b2->compare($b1));
*
* error_reporting($error_level); //restore errors
* ?>
* </code>
*
* @package tgiframework
* @subpackage debugging
* @author terry chay <tychay@php.net>
*/
// }}}
class tgif_benchmark_iterate
{
// PRIVATE INTERNALS
// {{{ - $_timer
/**
* The timer for the iteration
* @var tgif_benchmark_timer
*/
private $_timer;
// }}}
// {{{ - $_nullTimer
/**
* The timer that does a nop to compare it to
* @var tgif_benchmark_timer
*/
private $_nullTimer;
// }}}
// {{{ - $_numIteration
/**
* How many iterations did it do?
* @var integer
*/
private $_numIteration;
// }}}
// {{{ - $_functionName
/**
* The name of the function called (or a description if modified)
* @var string
*/
private $_functionName;
// }}}
// {{{ - $_defaultBehavior
/**
* If set to true, this will start and stop the timer on every iteration
* just like PEAR::Benchmark::Iterate. That setting is recommended in the
* case of doing a random data mark where the randomize function takes
* time to execute.
* @var string
*/
private $_defaultBehavior = false;
// }}}
// RESERVED METHODS
// {{{- __construct()
/**
* Constructor for object.
*
* @param boolean $trackRusage if true then it will track the resource usage
* also
* automatically
*/
public function __construct($trackRusage=false)
{
$this->_timer = new tgif_benchmark_timer(false,true);
$this->_nullTimer = new tgif_benchmark_timer(false,true);
}
// }}}
// COMMAND PATTERN
// {{{ - run()
/**
* Benchmark a function or method
*
* The parameters are done through func_get_args() so they are defined
* as follows:
* 1. (int) the number of iterations to execute
* 2. (mixed) the function to execute (will be parsed a la Benchmark_Iterate
* 3. ... arguments to provide to the function
*/
function run()
{
$args = func_get_args();
$max = (int) array_shift($args);
$function = array_shift($args);
$this->_functionName = self::_parse_callback($function);
$this->_numIteration = $max;
if ($this->_defaultBehavior) { // run timers each iteration
// clear the timers {{{
$this->_timer->start();
$this->_timer->stop();
$this->_nullTimer->start();
$this->_nullTimer->stop();
// }}}
// time run {{{
for ($i=0; $i<$max; ++$i) {
$this->_timer->start();
call_user_func_array($function, $args);
$this->_timer->stop(true);
}
// }}}
// time null {{{
for ($i=0; $i<$max; ++$i) {
$this->_nullTimer->start();
$this->_nullTimer->stop(true);
}
// }}}
} else { // time the overall thing only
// time run {{{
$this->_timer->start();
for ($i=0; $i<$max; ++$i) {
call_user_func_array($function, $args);
}
$this->_timer->stop();
// }}}
// time null {{{
$this->_nullTimer->start();
for ($i=0; $i<$max; ++$i) {
}
$this->_nullTimer->stop();
// }}}
}
}
// }}}
// {{{ - runGenerator()
/**
* Benchmark a function or method assuming the first parameter is a callback
* data generator on every iteration
*
* The parameters are done through func_get_args() so they are defined
* as follows:
* 1. (int) the number of iterations to execute
* 2. (mixed) the function to execute (will be parsed a la Benchmark_Iterate
* 3. first argument to provide callback to random argument generator
* 4... arguments go into random argument generator
*/
function runGenerator()
{
$args = func_get_args();
$max = (int) array_shift($args);
$function = array_shift($args);
$generator = array_shift($args);
$this->_functionName = self::_parse_callback($function);
$this->_numIteration = $max;
if ($this->_defaultBehavior) {
// clear the timers {{{
$this->_timer->start();
$this->_timer->stop();
$this->_nullTimer->start();
$this->_nullTimer->stop();
// }}}
// time run {{{
for ($i=0; $i<$max; ++$i) {
$fargs = call_user_func_array($generator, $args);
if (!is_array($fargs)) { $fargs = array($fargs); } // make sure we pass array into call_user_func_array();
$this->_timer->start();
call_user_func_array($function, $fargs);
$this->_timer->stop(true);
}
// }}}
// time null {{{
for ($i=0; $i<$max; ++$i) {
$fargs = call_user_func_array($generator, $args);
$this->_nullTimer->start();
$this->_nullTimer->stop(true);
}
// }}}
} else {
// time run {{{
$this->_timer->start();
for ($i=0; $i<$max; ++$i) {
$fargs = call_user_func_array($generator, $args);
if (!is_array($fargs)) { $fargs = array($fargs); } //makre sure we pass array into call_user_func_array();
call_user_func_array($function, $fargs);
}
$this->_timer->stop();
// }}}
// time null {{{
$this->_nullTimer->start();
for ($i=0; $i<$max; ++$i) {
$fargs = call_user_func_array($generator, $args);
}
$this->_nullTimer->stop();
// }}}
}
}
// }}}
// {{{ + _parse_callback($function)
/**
* Generate the function name and make the callback callable.
*
* @param mixed $function this will be transformed into a callable php
* function
* @return string the name of the callback for user rendering
*/
static private function _parse_callback(&$function)
{
if (is_string($function)) {
$return = $function.'()';
if (strstr($function, '::')) {
$function = explode('::', $function_name);
} elseif (strstr($function, '->')) {
$function = explode('->', $function_name);
$function[0] = $GLOBALS[$function[0]];
return '$'.$return;
}
return $return;
} else { //it must be an array
if (is_string($function[0])) {
return sprintf('%s::%s()', $function[0], $function[1]);
} else {
return sprintf('$_->%s()', $function[1]);
}
}
}
// }}}
// OUTPUT
// {{{ - compare()
/**
* Benchmark a function or method
*
* The parameters are done through func_get_args() where you can provide
* as many different comparisons as you want to other
* {@link tgif_benchmark_iterate}s.
* @return array information in human readable and computer parseable form
*/
function compare()
{
$args = func_get_args();
$bench_ref = $this->summary;
$count_ref = $bench_ref['count'];
$bench_ref['compare'] = '0';
$bench_ref['time_diff'] = 0;
$bench_ref['rtime_diff'] = 0;
$bench_ref['stime_diff'] = 0;
$bench_ref['utime_diff'] = 0;
$returns = array($bench_ref);
foreach ($args as $bench) {
$bench = $bench->summary;
$count = $bench['count'];
$faster = ($bench_ref['rtime'] * $count) - ($bench['rtime'] * $count_ref);
if ($faster > 0) {
$bench['compare'] = '+';
} elseif ($faster < 0) {
$bench['compare'] = '-';
} else {
$bench['compare'] = 0;
}
foreach (array('time','rtime','stime','utime') as $key) {
$ratio = 1 - abs(($bench[$key] * $count_ref) / ($bench_ref[$key] * $count + .000000001)); //the small number add prevents division by zero
// if it is positive, then flip the denominator
if ($ratio == 1) {
$ratio = 'Inf';
} elseif ($ratio > 0) {
$ratio = 1/(1-$ratio);
}
$bench[$key.'_diff'] = $ratio;
}
$returns[] = $bench;
}
return $returns;
}
// }}}
// {{{ + format($results)
/**
* Format a table of output from the compare function.
*
* Added mouseover with the actual times computed
* @param array $results Output from {@link compare()}.
* @return string HTML output
*/
static function format($results)
{
//var_dump($results);
$return = '<table class="benchmark_compare"><tr><th>mark</th><th>wall time</th><th>resource time</th></tr>';
// seek max and min times {{{
$times = array();
$rtimes = array();
foreach($results as $result) {
$times[] = $result['time']/$result['count'];
$rtimes[] = $result['rtime']/$result['count'];
}
$max_time = max($times);
$min_time = min($times);
$max_rtime = max($rtimes);
$min_rtime = min($rtimes);
// }}}
unset ($times); unset($rtimes);
// treat the first result special (it's a reference result) {{{
$result = array_shift($results);
$return .= sprintf(
'<tr><td>%s</td><td style="background:#%s">%02fs</td><td style="background:#%s">%02fs</td>',
$result['name'],
self::_hex_color($result['time']/$result['count'], $min_time, $max_time),
$result['time']/$result['count'],
self::_hex_color($result['rtime']/$result['count'],$min_rtime, $max_rtime),
$result['rtime']/$result['count']
);
// }}}
foreach ($results as $result) {
$return .= sprintf('<tr><td>%s</td><td style="background:#%s" title="%f">%.2fx</td><td style="background:#%s" title="%f">%.2fx</td>',
$result['name'],
self::_hex_color($result['time']/$result['count'], $min_time, $max_time),
$result['time']/$result['count'],
$result['time_diff'],
self::_hex_color($result['rtime']/$result['count'],$min_rtime, $max_rtime),
$result['rtime']/$result['count'],
$result['rtime_diff']
);
}
return $return . '</table>';
}
// }}}
// {{{ + _hex_color($value,$min,$max)
/**
* Turn a time into a color.
*
* @param float $value a value between $min and $max
* @param float $max the largest value in a spectrum ($value renders red)
* @param float $min the smallest value in a specturm ($value renders green)
* @return string the hex code of the output on a red yellow (really brown)
* green spectrum
*/
static private function _hex_color($value, $min, $max)
{
$percentage = (($max-$value) / ($max-$min)); //distance from fastest
return sprintf('%02x%02x00', 0xff*(1-$percentage), 0xff*$percentage);
}
// }}}
// ACCESSORS
// {{{ - __get($name)
/**
* Allow you to get values
* @return mixed if false you didn't record that value
*/
function __get($name)
{
switch (strtolower($name)) {
case 'numiterations':
case 'iterations':
case 'numiteration':
case 'count':
return $this->_numIteration;
case 'function':
case 'functionname':
case 'description':
return $this->_functionName;
case 'defaultbehavior':
case 'startstop':
return $this->_defaultBehavior;
case 'starttime':
case 'begintime':
case 'endtime':
case 'stoptime':
return $this->_timer->{$name};
case 'timetaken':
case 'timedifference':
case 'rtimetaken':
case 'stimetaken':
case 'utimetaken':
return tgif_benchmark_timer::bc_sub($this->_timer->{$name},$this->_nullTimer->{$name});
case 'summary':
return array(
'name' => $this->functionName,
'count' => $this->numIteration,
'time' => $this->timeTaken,
'rtime' => $this->rtimeTaken,
'stime' => $this->stimeTaken,
'utime' => $this->utimeTaken,
);
}
trigger_error(sprintf('Unknown property %s',$name), E_USER_WARNING);
}
// }}}
// {{{ - __set($name,$value)
/**
* Allow you to change the values
*/
function __set($name, $value)
{
switch (strtolower($name)) {
case 'function':
case 'functionname':
case 'description':
$this->_functionName = $value;
case 'defaultbehavior':
case 'startstop':
$this->_defaultBehavior = $value;
return;
}
trigger_error(sprintf('Unknown property %s',$name), E_USER_WARNING);
}
// }}}
}
// }}}
?>

Change log

r72 by tychay on Sep 30, 2010   Diff
 Issue 5 :
- figure out how to turn on xdebug errors
@done(2010-09-30)
 Issue 29 :
- bugs: when timers are off we get
division by zero and zero adds @done
- call_user_func_array() now needs array
@done(2010-09-30)
Go to: 

Older revisions

r62 by tychay on May 6, 2009   Diff
 Issue 30 : added random routine support
 Issue 30 : added old style start stop
timer
 Issue 29 : made math in benchmarking
system precise
...
r57 by tychay on May 6, 2009   Diff
 Issue 29 : commenting
r56 by tychay on May 6, 2009   Diff
 Issue 29 : created and tested
benchmarking code
All revisions of this file

File info

Size: 14684 bytes, 426 lines
Powered by Google Project Hosting