Completed
Push — master ( 84dee3...084275 )
by Michal
36:03
created

BenchmarkShell::main()   B

Complexity

Conditions 4
Paths 2

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 22
rs 8.9197
cc 4
eloc 15
nc 2
nop 0
1
<?php
2
/**
3
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
4
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
5
 *
6
 * Licensed under The MIT License
7
 * Redistributions of files must retain the above copyright notice.
8
 *
9
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
10
 * @link          http://cakephp.org CakePHP(tm) Project
11
 * @since         DebugKit 1.0
12
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
13
 */
14
namespace DebugKit\Shell;
15
16
use Cake\Console\Shell;
17
use Cake\Utility\Text;
18
19
/**
20
 * Benchmark Shell Class
21
 *
22
 * Provides basic benchmarking of application requests
23
 * functionally similar to Apache AB
24
 *
25
 * @since         DebugKit 1.0
26
 * @todo Print/export time detail information
27
 * @todo Export/graphing of data to .dot format for graphviz visualization
28
 * @todo Make calculated results round to leading significant digit position of std dev.
29
 */
30
class BenchmarkShell extends Shell
31
{
32
33
    /**
34
     * Main execution of shell
35
     *
36
     * @return void
37
     */
38
    public function main()
39
    {
40
        $url = $this->args[0];
41
        $defaults = ['t' => 100, 'n' => 10];
42
        $options = array_merge($defaults, $this->params);
43
        $times = [];
44
45
        $this->out(Text::insert(__d('debug_kit', '-> Testing :url'), compact('url')));
46
        $this->out("");
47
        for ($i = 0; $i < $options['n']; $i++) {
48
            if (floor($options['t'] - array_sum($times)) <= 0 || $options['n'] <= 1) {
49
                break;
50
            }
51
52
            $start = microtime(true);
53
            file_get_contents($url);
54
            $stop = microtime(true);
55
56
            $times[] = $stop - $start;
57
        }
58
        $this->_results($times);
59
    }
60
61
    /**
62
     * Prints calculated results
63
     *
64
     * @param array $times Array of time values
65
     * @return void
66
     */
67
    protected function _results($times)
68
    {
69
        $duration = array_sum($times);
70
        $requests = count($times);
71
72
        $this->out(Text::insert(__d('debug_kit', 'Total Requests made: :requests'), compact('requests')));
73
        $this->out(Text::insert(__d('debug_kit', 'Total Time elapsed: :duration (seconds)'), compact('duration')));
74
75
        $this->out("");
76
77
        $this->out(Text::insert(__d('debug_kit', 'Requests/Second: :rps req/sec'), [
78
                'rps' => round($requests / $duration, 3)
79
        ]));
80
81
        $this->out(Text::insert(__d('debug_kit', 'Average request time: :average-time seconds'), [
82
                'average-time' => round($duration / $requests, 3)
83
        ]));
84
85
        $this->out(Text::insert(__d('debug_kit', 'Standard deviation of average request time: :std-dev'), [
86
                'std-dev' => round($this->_deviation($times, true), 3)
87
        ]));
88
89
        $this->out(Text::insert(__d('debug_kit', 'Longest/shortest request: :longest sec/:shortest sec'), [
90
                'longest' => round(max($times), 3),
91
                'shortest' => round(min($times), 3)
92
        ]));
93
94
        $this->out("");
95
    }
96
97
    /**
98
     * One-pass, numerically stable calculation of population variance.
99
     *
100
     * Donald E. Knuth (1998).
101
     * The Art of Computer Programming, volume 2: Seminumerical Algorithms, 3rd edn.,
102
     * p. 232. Boston: Addison-Wesley.
103
     *
104
     * @param array $times Array of values
105
     * @param bool $sample If true, calculates an unbiased estimate of the population
106
     *                           variance from a finite sample.
107
     * @return float Variance
108
     */
109
    protected function _variance($times, $sample = true)
110
    {
111
        $n = $mean = $M2 = 0;
112
113
        foreach ($times as $time) {
114
            $n += 1;
115
            $delta = $time - $mean;
116
            $mean = $mean + $delta / $n;
117
            $M2 = $M2 + $delta * ($time - $mean);
118
        }
119
120
        if ($sample) {
121
            $n -= 1;
122
        }
123
124
        return $M2 / $n;
125
    }
126
127
    /**
128
     * Calculate the standard deviation.
129
     *
130
     * @param array $times Array of values
131
     * @param bool $sample ''
132
     * @return float Standard deviation
133
     */
134
    protected function _deviation($times, $sample = true)
135
    {
136
        return sqrt($this->_variance($times, $sample));
137
    }
138
139
    /**
140
     * Get option parser.
141
     *
142
     * @return \Cake\Console\OptionParser
143
     */
144
    public function getOptionParser()
145
    {
146
        $parser = parent::getOptionParser();
147
        $parser->description(__d(
148
            'debug_kit',
149
            'Allows you to obtain some rough benchmarking statistics' .
150
            'about a fully qualified URL.'
151
        ))
152
        ->addArgument('url', [
153
            'help' => __d('debug_kit', 'The URL to request.'),
154
            'required' => true
155
        ])
156
        ->addOption('n', [
157
            'default' => 10,
158
            'help' => __d('debug_kit', 'Number of iterations to perform.')
159
        ])
160
        ->addOption('t', [
161
            'default' => 100,
162
            'help' => __d(
163
                'debug_kit',
164
                'Maximum total time for all iterations, in seconds.' .
165
                'If a single iteration takes more than the timeout, only one request will be made'
166
            )
167
        ])
168
        ->epilog(__d(
169
            'debug_kit',
170
            'Example Use: `cake benchmark --n 10 --t 100 http://localhost/testsite`. ' .
171
            '<info>Note:</info> this benchmark does not include browser render times.'
172
        ));
173
        return $parser;
174
    }
175
}
176