1 | <?php |
||
2 | /** |
||
3 | * Metrics type "summary" |
||
4 | * User: moyo |
||
5 | * Date: 2018/5/17 |
||
6 | * Time: 11:06 PM |
||
7 | * @see http://infolab.stanford.edu/~datar/courses/cs361a/papers/quantiles.pdf |
||
8 | */ |
||
9 | |||
10 | namespace Carno\Monitor\Metrics; |
||
11 | |||
12 | use Carno\Monitor\Chips\Metrical\Labeled; |
||
13 | use Carno\Monitor\Chips\Metrical\Named; |
||
14 | use Carno\Monitor\Chips\Metrical\Reporting; |
||
15 | use Carno\Monitor\Chips\Metrical\SExporter; |
||
16 | use Carno\Monitor\Chips\Metrical\SQuantiles; |
||
17 | use Carno\Monitor\Contracts\HMRegistry; |
||
18 | use Carno\Monitor\Contracts\RAWMetrics; |
||
19 | use Carno\Monitor\Contracts\Telemetry; |
||
20 | |||
21 | class Summary implements RAWMetrics, HMRegistry, Telemetry |
||
22 | { |
||
23 | use Named, Labeled, SQuantiles, SExporter, Reporting; |
||
24 | |||
25 | /** |
||
26 | * @var float |
||
27 | */ |
||
28 | private $epsilon = 0; |
||
29 | |||
30 | /** |
||
31 | * @var int |
||
32 | */ |
||
33 | private $threshold = 0; |
||
34 | |||
35 | /** |
||
36 | * @var int |
||
37 | */ |
||
38 | private $count = 0; |
||
39 | |||
40 | /** |
||
41 | * @var float |
||
42 | */ |
||
43 | private $sum = 0; |
||
44 | |||
45 | /** |
||
46 | * @var SItem[] |
||
47 | */ |
||
48 | private $samples = []; |
||
49 | |||
50 | /** |
||
51 | * Summary constructor. |
||
52 | * @param float $epsilon |
||
53 | * @param int $threshold |
||
54 | */ |
||
55 | public function __construct(float $epsilon = 0.001, int $threshold = 2000) |
||
56 | { |
||
57 | $this->epsilon = $epsilon; |
||
58 | $this->threshold = $threshold; |
||
59 | } |
||
60 | |||
61 | /** |
||
62 | * @param float $v |
||
63 | */ |
||
64 | public function observe(float $v) : void |
||
65 | { |
||
66 | $rank = $this->ranked($v); |
||
67 | |||
68 | $delta = $rank === 0 || $rank === count($this->samples) ? 0 : floor(2 * $this->epsilon * $this->count); |
||
69 | |||
70 | array_splice($this->samples, $rank, 0, [new SItem($v, 1, $delta)]); |
||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
71 | |||
72 | count($this->samples) > $this->threshold && $this->compress(); |
||
73 | |||
74 | $this->count ++; |
||
75 | $this->sum += $v; |
||
76 | } |
||
77 | |||
78 | /** |
||
79 | * @param float $quantile |
||
80 | * @return float |
||
81 | */ |
||
82 | private function query(float $quantile) : float |
||
0 ignored issues
–
show
|
|||
83 | { |
||
84 | $rank = 0; |
||
85 | |||
86 | $desired = intval($quantile * $this->count); |
||
87 | |||
88 | for ($i = 1; $i < count($this->samples); $i ++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
89 | $sp = $this->samples[$i - 1]; |
||
90 | $sc = $this->samples[$i]; |
||
91 | |||
92 | if (($rank += $sp->g) + $sc->g + $sc->d > $desired + (2 * $this->epsilon * $this->count)) { |
||
93 | return $sp->v; |
||
94 | } |
||
95 | } |
||
96 | |||
97 | return end($this->samples)->v; |
||
98 | } |
||
99 | |||
100 | /** |
||
101 | */ |
||
102 | private function compress() : void |
||
103 | { |
||
104 | for ($i = 0; $i < count($this->samples) - 1; $i ++) { |
||
105 | $sa = $this->samples[$i]; |
||
106 | $sb = $this->samples[$i + 1]; |
||
107 | |||
108 | if ($sa->g + $sb->g + $sb->d <= floor(2 * $this->epsilon * $this->count)) { |
||
109 | $sb->g += $sa->g; |
||
110 | array_splice($this->samples, $i, 1); |
||
111 | } |
||
112 | } |
||
113 | } |
||
114 | |||
115 | /** |
||
116 | * @param float $v |
||
117 | * @return int |
||
118 | */ |
||
119 | private function ranked(float $v) : int |
||
120 | { |
||
121 | if (empty($this->samples)) { |
||
122 | return 0; |
||
123 | } |
||
124 | |||
125 | $start = $rank = 0; |
||
126 | $end = count($this->samples) - 1; |
||
127 | |||
128 | while ($start <= $end) { |
||
129 | $rank = intval(($start + $end) / 2); |
||
130 | if (($curr = $this->samples[$rank]->v) < $v) { |
||
131 | $start = $rank + 1; |
||
132 | } elseif ($curr > $v) { |
||
133 | $end = $rank - 1; |
||
134 | } else { |
||
135 | return $rank; |
||
136 | } |
||
137 | } |
||
138 | |||
139 | return $rank; |
||
140 | } |
||
141 | } |
||
142 |