Completed
Push — master ( 23d6c4...eb4d77 )
by Alec
06:36
created

Benchmark::computeRelatives()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 4
nop 2
dl 0
loc 14
ccs 10
cts 10
cp 1
crap 3
rs 9.9666
c 0
b 0
f 0
1
<?php
2
/**
3
 * User: alec
4
 * Date: 29.11.18
5
 * Time: 11:04
6
 */
7
8
namespace AlecRabbit\Tools;
9
10
use AlecRabbit\Profiler\Profiler;
11
use AlecRabbit\Profiler\Timer;
12
use AlecRabbit\Rewindable;
13
14
class Benchmark
15
{
16
    /** @var array */
17
    private $functions = [];
18
    /** @var Rewindable */
19
    private $iterations;
20
    /** @var Profiler */
21
    private $profiler;
22
    /** @var int */
23
    private $namingIndex;
24
    /** @var null|string */
25
    private $tmpName;
26
27 3
    public function __construct(int $iterations = 1000)
28
    {
29 3
        $this->iterations =
30 3
            new Rewindable(
31
                function (int $iterations): \Generator {
32 1
                    $i = 1;
33 1
                    while ($i <= $iterations) {
34 1
                        yield $i++;
35
                    }
36 3
                },
37 3
                $iterations
38
            );
39 3
        $this->profiler = new Profiler();
40 3
        $this->namingIndex = 0;
41 3
    }
42
43 1
    public function compare(): void
44
    {
45 1
        foreach ($this->functions as $name => $f) {
46 1
            $this->profiler->timer($name)->start();
47 1
            foreach ($this->iterations as $iteration) {
48 1
                [$function, $args] = $f;
49
                /** @noinspection VariableFunctionsUsageInspection */
50 1
                \call_user_func($function, ...$args);
51
                /** @noinspection DisconnectedForeachInstructionInspection */
52 1
                $this->profiler->timer($name)->check();
53
            }
54
        }
55 1
    }
56
57
    /**
58
     * @param callable $func
59
     * @param mixed ...$args
60
     */
61 2
    public function addFunction($func, ...$args): void
62
    {
63
64 2
        if (!\is_callable($func, false, $callable_name)) {
65 1
            throw new \InvalidArgumentException('Function must be callable');
66
        }
67 1
        if (null !== $this->tmpName) {
68 1
            $callable_name = $this->tmpName;
69 1
            $this->tmpName = null;
70
        }
71 1
        if (array_key_exists($callable_name, $this->functions)) {
72 1
            $callable_name .= '_' . ++$this->namingIndex;
73
        }
74 1
        $this->functions[$callable_name] = [$func, $args];
75 1
    }
76
77 1
    public function report(): array
78
    {
79 1
        $timers = $this->profiler->getTimers();
80 1
        $averages = $this->computeAverages($timers);
81
82 1
        $min = min($averages);
83
        return
84 1
            $this->computeRelatives($averages, $min);
85
    }
86
87
    /**
88
     * @param array $timers
89
     * @return array
90
     */
91 1
    private function computeAverages(array $timers): array
92
    {
93 1
        $averages = [];
94
        /** @var Timer $timer */
95 1
        foreach ($timers as $timer) {
96 1
            $averages[$timer->getName()] = $timer->getAvgValue();
97
        }
98 1
        return $averages;
99
    }
100
101
    /**
102
     * @param array $averages
103
     * @param float $min
104
     * @return array
105
     */
106 1
    private function computeRelatives(array $averages, float $min): array
107
    {
108 1
        $relatives = [];
109 1
        foreach ($averages as $name => $average) {
110 1
            $relatives[$name] = $average / $min;
111
        }
112 1
        asort($relatives);
113
114 1
        foreach ($relatives as $name => $relative) {
115 1
            $relatives[$name] =
116 1
                $this->toPercentage($relative) . ' ' .
117 1
                brackets(format_time($averages[$name]), BRACKETS_PARENTHESES);
118
        }
119 1
        return $relatives;
120
    }
121
122 1
    private function toPercentage(float $relative): string
123
    {
124
        return
125 1
            number_format($relative * 100, 1) . '%';
126
    }
127
128 2
    public function profilerReport(
129
        bool $formatted = true,
130
        bool $extended = true,
131
        ?int $units = null,
132
        ?int $precision = null
133
    ): iterable {
134
        return
135 2
            $this->profiler->report($formatted, $extended, $units, $precision);
136
    }
137
138 2
    public function withName(string $name): self
139
    {
140 2
        $this->tmpName = $name;
141 2
        return $this;
142
    }
143
}
144