Passed
Push — master ( 166b7f...cf4991 )
by Alec
05:01
created

Benchmark::prepareResult()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2.1481

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 3
dl 0
loc 5
ccs 2
cts 3
cp 0.6667
crap 2.1481
rs 10
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\Rewindable;
11
use AlecRabbit\Tools\Contracts\BenchmarkInterface;
12
use AlecRabbit\Tools\Internal\BenchmarkFunction;
13
use AlecRabbit\Tools\Reports\Contracts\ReportableInterface;
14
use AlecRabbit\Tools\Reports\Traits\Reportable;
15
use AlecRabbit\Tools\Traits\BenchmarkFields;
16
use function AlecRabbit\typeOf;
17
18
class Benchmark implements BenchmarkInterface, ReportableInterface
19
{
20
    use BenchmarkFields, Reportable;
21
22
    /** @var int */
23
    private $namingIndex = 0;
24
    /** @var Rewindable */
25
    private $rewindable;
26
    /** @var int */
27
    private $iterations = 0;
28
    /** @var null|string */
29
    private $comment;
30
    /** @var bool */
31
    private $verbose = false;
32
33
    /**
34
     * Benchmark constructor.
35
     * @param int $iterations
36
     */
37 3
    public function __construct(int $iterations = 1000)
38
    {
39 3
        $this->iterations = $iterations;
40 3
        $this->rewindable =
41 3
            new Rewindable(
42
                function (int $iterations, int $i = 1): \Generator {
43 1
                    while ($i <= $iterations) {
44 1
                        yield $i++;
45
                    }
46 3
                },
47 3
                $iterations
48
            );
49 3
        $this->profiler = new Profiler();
50 3
    }
51
52
    /**
53
     * Launch benchmarking
54
     * @param bool $report
55
     */
56 1
    public function run(bool $report = false): void
57
    {
58 1
        if ($this->verbose) {
59
            $this->verboseRun();
60
        } else {
61 1
            $this->nonVerboseRun();
62
        }
63 1
        if ($report) {
64
            echo (string)$this->getReport();
65
            echo PHP_EOL;
66
        }
67 1
    }
68
69
    /**
70
     * Launch benchmarking in verbose mode
71
     */
72
    private function verboseRun(): void
73
    {
74
        echo
75
        sprintf(
76
            'Running benchmarks(%s):',
77
            $this->iterations
78
        );
79
        echo PHP_EOL;
80
        /** @var  BenchmarkFunction $f */
81
        foreach ($this->functions as $name => $f) {
82
            $function = $f->getFunction();
83
            $args = $f->getArgs();
84
            $this->prepareResult($f, $function, $args);
85
            $timer = $this->profiler->timer($name);
86
            $timer->start();
87
            foreach ($this->rewindable as $iteration) {
88
                /** @noinspection VariableFunctionsUsageInspection */
89
                /** @noinspection DisconnectedForeachInstructionInspection */
90
                \call_user_func($function, ...$args);
91
                $timer->check($iteration);
92
                ++$this->totalIterations;
93
                if (1 === $this->totalIterations % 5000) {
94
                    echo '.';
95
                }
96
            }
97
            $this->profiler->counter()->bump();
98
        }
99
        echo PHP_EOL;
100
        echo PHP_EOL;
101
    }
102
103
    /**
104
     * @param BenchmarkFunction $f
105
     * @param callable $function
106
     * @param array $args
107
     */
108 1
    private function prepareResult(BenchmarkFunction $f, callable $function, array $args): void
109
    {
110 1
        if ($this->withResults) {
111
            /** @noinspection VariableFunctionsUsageInspection */
112
            $f->setResult(\call_user_func($function, ...$args));
113
        }
114 1
    }
115
116
    /**
117
     * Launch benchmarking in verbose mode
118
     */
119 1
    private function nonVerboseRun(): void
120
    {
121
        /** @var  BenchmarkFunction $f */
122 1
        foreach ($this->functions as $name => $f) {
123 1
            $function = $f->getFunction();
124 1
            $args = $f->getArgs();
125 1
            $this->prepareResult($f, $function, $args);
126 1
            $timer = $this->profiler->timer($name);
127 1
            $timer->start();
128 1
            foreach ($this->rewindable as $iteration) {
129
                /** @noinspection VariableFunctionsUsageInspection */
130
                /** @noinspection DisconnectedForeachInstructionInspection */
131 1
                \call_user_func($function, ...$args);
132 1
                $timer->check($iteration);
133 1
                ++$this->totalIterations;
134
            }
135 1
            $this->profiler->counter()->bump();
136
        }
137 1
    }
138
139
    /**
140
     * @return Benchmark
141
     */
142
    public function verbose(): self
143
    {
144
        $this->verbose = true;
145
        return $this;
146
    }
147
148
    /**
149
     * @param callable $func
150
     * @param mixed ...$args
151
     */
152 2
    public function addFunction($func, ...$args): void
153
    {
154 2
        if (!\is_callable($func, false, $name)) {
155 1
            throw new \InvalidArgumentException(
156 1
                sprintf(
157 1
                    '\'%s\' is NOT callable. Function must be callable. Type of "%s" provided instead.',
158 1
                    $name,
159 1
                    typeOf($func)
160
                )
161
            );
162
        }
163 1
        $function = new BenchmarkFunction($func, $name, $this->namingIndex++, $args, $this->comment);
164 1
        $this->comment = null;
165
166 1
        $this->functions[$function->getEnumeratedName()] = $function;
167 1
    }
168
169
    /**
170
     * @param string $name
171
     * @return Benchmark
172
     */
173 2
    public function withComment(string $name): self
174
    {
175 2
        $this->comment = $name;
176 2
        return $this;
177
    }
178
179
    /**
180
     * @return Benchmark
181
     */
182
    public function returnResults(): self
183
    {
184
        $this->withResults = true;
185
        return $this;
186
    }
187
188
    /**
189
     * @return string
190
     */
191
    public function elapsed(): string
192
    {
193
        return
194
            sprintf(
195
                'Done in: %s',
196
                $this->getProfiler()->timer()->elapsed()
197
            );
198
    }
199
200
    /**
201
     * {@inheritdoc}
202
     */
203 2
    protected function prepareForReport(): void
204
    {
205 2
        $this->getProfiler()->getReport();
206 2
    }
207
}
208