Passed
Push — master ( 06b5e4...27aa0e )
by Eric
01:37
created

Bench::hasStarted()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Bench - Micro PHP library for benchmarking.
7
 *
8
 * @author    Eric Sizemore <[email protected]>
9
 * @version   3.0.0
10
 * @copyright (C) 2024 Eric Sizemore
11
 * @license   The MIT License (MIT)
12
 *
13
 * Copyright (C) 2024 Eric Sizemore<https://www.secondversion.com/>.
14
 * Copyright (C) 2012-2020 Jeremy Perret<[email protected]>
15
 *
16
 * Permission is hereby granted, free of charge, to any person obtaining a copy
17
 * of this software and associated documentation files (the "Software"), to
18
 * deal in the Software without restriction, including without limitation the
19
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
20
 * sell copies of the Software, and to permit persons to whom the Software is
21
 * furnished to do so, subject to the following conditions:
22
 *
23
 * The above copyright notice and this permission notice shall be included in
24
 * all copies or substantial portions of the Software.
25
 *
26
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32
 * THE SOFTWARE.
33
 */
34
35
/**
36
 * Esi\Bench is a fork of Ubench (https://github.com/devster/ubench) which is:
37
 *     Copyright (c) 2012-2020 Jeremy Perret<[email protected]>
38
 *
39
 * For a list of changes in this library, in comparison to the original library, please {@see CHANGELOG.md}.
40
 */
41
42
namespace Esi\Bench;
43
44
use LogicException;
45
46
use function memory_get_usage;
47
use function memory_get_peak_usage;
48
use function hrtime;
49
use function preg_replace;
50
use function round;
51
use function sprintf;
52
53
/**
54
 * Micro PHP library for benchmarking.
55
 *
56
 * @see \Esi\Bench\Tests\BenchTest
57
 */
58
class Bench implements BenchInterface
59
{
60
    /**
61
     * Start time in nanoseconds.
62
     */
63
    protected float $startTime;
64
65
    /**
66
     * End time in nanoseconds.
67
     */
68
    protected float $endTime;
69
70
    /**
71
     * Memory usage.
72
     */
73
    protected int $memoryUsage;
74
75
    /**
76
     * {@inheritdoc}
77
     */
78 8
    public function start(): void
79
    {
80 8
        $this->startTime = hrtime(true);
81
    }
82
83
    /**
84
     * {@inheritdoc}
85
     */
86 6
    public function end(): self
87
    {
88 6
        if (!$this->hasStarted()) {
89 1
            throw new LogicException('Bench has not been started. Call start() first.');
90
        }
91
92 5
        $this->endTime     = hrtime(true);
93 5
        $this->memoryUsage = memory_get_usage(true);
94
95 5
        return $this;
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101 3
    public function getTime(bool $readable = false, string | null $format = null): float | string
102
    {
103 3
        if (!$this->hasStarted()) {
104 1
            throw new LogicException('Bench has not been started. Call start() first.');
105
        }
106
107 2
        if (!$this->hasEnded()) {
108 1
            throw new LogicException('Bench has not been ended. Call end() first.');
109
        }
110
111
        // Convert to seconds
112 1
        $elapsed = ($this->endTime - $this->startTime) / 1e9;
113
114 1
        if ($readable) {
115 1
            return $elapsed;
116
        }
117
118 1
        return self::readableElapsedTime($elapsed, $format);
119
    }
120
121
    /**
122
     * {@inheritdoc}
123
     */
124 3
    public function getMemoryUsage(bool $readable = false, string | null $format = null): int | string
125
    {
126 3
        if (!$this->hasStarted()) {
127 1
            throw new LogicException('Bench has not been started. Call start() first.');
128
        }
129
130 2
        if (!$this->hasEnded()) {
131 1
            throw new LogicException('Bench has not been ended. Call end() first.');
132
        }
133
134 1
        if ($readable) {
135 1
            return $this->memoryUsage;
136
        }
137
138 1
        return self::readableSize($this->memoryUsage, $format);
139
    }
140
141
    /**
142
     * {@inheritdoc}
143
     */
144 1
    public function getMemoryPeak(bool $readable = false, string | null $format = null): string | int
145
    {
146 1
        $memory = memory_get_peak_usage(true);
147
148 1
        if ($readable) {
149 1
            return $memory;
150
        }
151
152 1
        return self::readableSize($memory, $format);
153
    }
154
155
    /**
156
     * {@inheritdoc}
157
     */
158 2
    public function run(callable $callable, mixed ...$arguments): mixed
159
    {
160 2
        $this->start();
161 2
        $result = $callable(...$arguments);
162 2
        $this->end();
163
164 2
        return $result;
165
    }
166
167
    /**
168
     * {@inheritdoc}
169
     */
170 14
    public static function readableSize(int $size, string | null $format = null, int $round = 3): string
171
    {
172 14
        static $units = ['B', 'KB', 'MB', 'GB', 'TB'];
173 14
        static $mod   = 1024;
174
175 14
        $format ??= '%.2f%s';
176
177 14
        if ($size <= $mod) {
178 4
            return sprintf('%dB', round($size, $round));
179
        }
180
181 10
        $unit = 0;
182
183
        do {
184 10
            ++$unit;
185 10
            $size /= $mod;
186 10
        } while($size > $mod);
187
188 10
        return sprintf($format, round($size, $round), $units[$unit]);
189
    }
190
191
    /**
192
     * {@inheritdoc}
193
     */
194 3
    public static function readableElapsedTime(float $seconds, string | null $format = null, int $round = 3): string
195
    {
196 3
        $format ??= '%.3f%s';
197
198 3
        if ($seconds >= 1) {
199 2
            return sprintf($format, round($seconds, $round), 's');
200
        }
201
202 2
        $format = (string) preg_replace('/(%.\d+f)/', '%d', $format);
0 ignored issues
show
Unused Code introduced by
The assignment to $format is dead and can be removed.
Loading history...
203
204 2
        return sprintf('%d%s', round($seconds * 1000, $round), 'ms');
205
    }
206
207
    /**
208
     * {@inheritdoc}
209
     */
210 5
    public function hasEnded(): bool
211
    {
212 5
        return isset($this->endTime);
213
    }
214
215
    /**
216
     * {@inheritdoc}
217
     */
218 11
    public function hasStarted(): bool
219
    {
220 11
        return isset($this->startTime);
221
    }
222
}
223