Completed
Push — develop ( 6b618f...2007a6 )
by Alec
04:06
created

Benchmark::nonVerboseRun()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 11
nc 3
nop 0
dl 0
loc 17
ccs 12
cts 12
cp 1
crap 3
rs 9.9
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->report();
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
     * Launch benchmarking in verbose mode
105
     */
106 1
    private function nonVerboseRun(): void
107
    {
108
        /** @var  BenchmarkFunction $f */
109 1
        foreach ($this->functions as $name => $f) {
110 1
            $function = $f->getFunction();
111 1
            $args = $f->getArgs();
112 1
            $this->prepareResult($f, $function, $args);
113 1
            $timer = $this->profiler->timer($name);
114 1
            $timer->start();
115 1
            foreach ($this->rewindable as $iteration) {
116
                /** @noinspection VariableFunctionsUsageInspection */
117
                /** @noinspection DisconnectedForeachInstructionInspection */
118 1
                \call_user_func($function, ...$args);
119 1
                $timer->check($iteration);
120 1
                ++$this->totalIterations;
121
            }
122 1
            $this->profiler->counter()->bump();
123
        }
124 1
    }
125
126
    /**
127
     * @return Benchmark
128
     */
129
    public function verbose(): self
130
    {
131
        $this->verbose = true;
132
        return $this;
133
    }
134
135
    /**
136
     * @param callable $func
137
     * @param mixed ...$args
138
     */
139 2
    public function addFunction($func, ...$args): void
140
    {
141 2
        if (!\is_callable($func, false, $name)) {
142 1
            throw new \InvalidArgumentException(
143 1
                sprintf(
144 1
                    '\'%s\' is NOT callable. Function must be callable. Type of "%s" provided instead.',
145 1
                    $name,
146 1
                    typeOf($func)
147
                )
148
            );
149
        }
150 1
        $function = new BenchmarkFunction($func, $name, $this->namingIndex++, $args, $this->comment);
151 1
        $this->comment = null;
152
153 1
        $this->functions[$function->getEnumeratedName()] = $function;
154 1
    }
155
156
    /**
157
     * @param string $name
158
     * @return Benchmark
159
     */
160 2
    public function withComment(string $name): self
161
    {
162 2
        $this->comment = $name;
163 2
        return $this;
164
    }
165
166
    /**
167
     * @return Benchmark
168
     */
169
    public function returnResults(): self
170
    {
171
        $this->withResults = true;
172
        return $this;
173
    }
174
175 2
    protected function prepareForReport(): void
176
    {
177 2
        $this->getProfiler()->report();
178 2
    }
179
180
    /**
181
     * @param BenchmarkFunction $f
182
     * @param callable $function
183
     * @param array $args
184
     */
185 1
    private function prepareResult(BenchmarkFunction $f, callable $function, array $args): void
186
    {
187 1
        if ($this->withResults) {
188
            /** @noinspection VariableFunctionsUsageInspection */
189
            $f->setResult(\call_user_func($function, ...$args));
190
        }
191 1
    }
192
}
193