Skip to content

Commit 5849576

Browse files
authored
Merge pull request #2 from BinarCode/profiling
profiling
2 parents 32fa907 + 040ff63 commit 5849576

File tree

7 files changed

+325
-2
lines changed

7 files changed

+325
-2
lines changed

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,49 @@ By default, all entries older than 24 hours will be pruned. You may use the hour
175175
$schedule->command('dev:prune --hours=48')->daily();
176176
```
177177

178+
179+
## Profiling
180+
181+
As a developer sometimes you have to measure the memory usage or time consumed for such action. Laravel Developer helps you to do so:
182+
183+
```php
184+
measure_memory(function() {
185+
// some code
186+
});
187+
```
188+
189+
Or:
190+
191+
```php
192+
$timing = \Binarcode\LaravelDeveloper\Profiling\ServerTiming::startWithoutKey();
193+
194+
sleep(1);
195+
196+
$timing->stopAllUnfinishedEvents();
197+
198+
$timing->getDuration();
199+
```
200+
201+
And time measure:
202+
203+
```php
204+
measure_timing(function() {
205+
// some code
206+
})
207+
```
208+
209+
Or:
210+
211+
```php
212+
$memory = \Binarcode\LaravelDeveloper\Profiling\ServerMemory::measure();
213+
214+
// some code memory consuming
215+
216+
$memory->stop();
217+
218+
$memory->getMemory();
219+
```
220+
178221
## Testing
179222

180223
``` bash

composer.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
"require": {
1919
"php": "^7.4|^8.0",
2020
"illuminate/contracts": "^8.0",
21-
"laravel/slack-notification-channel": "^2.3"
21+
"illuminate/support": "^7.0|^8.0",
22+
"laravel/slack-notification-channel": "^2.3",
23+
"symfony/stopwatch": "^3.4"
2224
},
2325
"require-dev": {
2426
"orchestra/testbench": "^6.0",
@@ -29,7 +31,10 @@
2931
"psr-4": {
3032
"Binarcode\\LaravelDeveloper\\": "src",
3133
"Binarcode\\LaravelDeveloper\\Database\\Factories\\": "database/factories"
32-
}
34+
},
35+
"files": [
36+
"src/helpers.php"
37+
]
3338
},
3439
"autoload-dev": {
3540
"psr-4": {

src/Profiling/ServerMemory.php

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
namespace Binarcode\LaravelDeveloper\Profiling;
4+
5+
class ServerMemory
6+
{
7+
/** @var array */
8+
protected $finishedEvents = [];
9+
10+
/** @var array */
11+
protected $startedEvents = [];
12+
13+
public function start(string $key = 'measure')
14+
{
15+
$this->startedEvents[$key] = memory_get_usage();
16+
17+
return $this;
18+
}
19+
20+
public function stop(string $key = 'measure')
21+
{
22+
if (! array_key_exists($key, $this->startedEvents)) {
23+
return $this;
24+
}
25+
26+
$this->setMemory(
27+
$key,
28+
memory_get_usage() - $this->startedEvents[$key]
29+
);
30+
31+
unset($this->startedEvents[$key]);
32+
33+
return $this;
34+
}
35+
36+
public function setMemory(string $key, $duration)
37+
{
38+
if (is_callable($duration)) {
39+
$this->start($key);
40+
41+
call_user_func($duration);
42+
43+
$this->stop($key);
44+
} else {
45+
$this->finishedEvents[$key] = $duration;
46+
}
47+
48+
return $this;
49+
}
50+
51+
public function getMemory(string $key = 'measure', $unit = 'mb')
52+
{
53+
return $this->finishedEvents[$key]
54+
? collect([
55+
56+
'byte' => fn ($v) => $v,
57+
'mb' => fn ($v) => $v / (1000 * 1000),
58+
'gb' => fn ($v) => $v / (1000 * 1000 * 1000),
59+
60+
])->get($unit, 'mb')($this->finishedEvents[$key])
61+
: null;
62+
}
63+
64+
public static function measure(string $key = 'measure'): ServerMemory
65+
{
66+
return app(static::class)->start($key);
67+
}
68+
}

src/Profiling/ServerTiming.php

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?php
2+
3+
namespace Binarcode\LaravelDeveloper\Profiling;
4+
5+
use Symfony\Component\Stopwatch\Stopwatch;
6+
7+
class ServerTiming
8+
{
9+
/** @var Stopwatch */
10+
protected $stopwatch;
11+
12+
/** @var array */
13+
protected $finishedEvents = [];
14+
15+
/** @var array */
16+
protected $startedEvents = [];
17+
18+
public function __construct(Stopwatch $stopwatch)
19+
{
20+
$this->stopwatch = $stopwatch;
21+
}
22+
23+
public function addMetric(string $metric)
24+
{
25+
$this->finishedEvents[$metric] = null;
26+
27+
return $this;
28+
}
29+
30+
public function hasStartedEvent(string $key): bool
31+
{
32+
return array_key_exists($key, $this->startedEvents);
33+
}
34+
35+
public function measure(string $key)
36+
{
37+
if (! $this->hasStartedEvent($key)) {
38+
return $this->start($key);
39+
}
40+
41+
return $this->stop($key);
42+
}
43+
44+
public function start(string $key = 'measure')
45+
{
46+
$this->stopwatch->start($key);
47+
48+
$this->startedEvents[$key] = true;
49+
50+
return $this;
51+
}
52+
53+
public function stop(string $key = 'measure')
54+
{
55+
if ($this->stopwatch->isStarted($key)) {
56+
$event = $this->stopwatch->stop($key);
57+
58+
$this->setDuration($key, $event->getDuration());
59+
60+
unset($this->startedEvents[$key]);
61+
}
62+
63+
return $this;
64+
}
65+
66+
public function stopAllUnfinishedEvents()
67+
{
68+
foreach (array_keys($this->startedEvents) as $startedEventName) {
69+
$this->stop($startedEventName);
70+
}
71+
}
72+
73+
public function setDuration(string $key, $duration)
74+
{
75+
if (is_callable($duration)) {
76+
$this->start($key);
77+
78+
call_user_func($duration);
79+
80+
$this->stop($key);
81+
} else {
82+
$this->finishedEvents[$key] = $duration;
83+
}
84+
85+
return $this;
86+
}
87+
88+
public function getDuration(string $key = 'measure', $unit = 's')
89+
{
90+
return $this->finishedEvents[$key]
91+
? collect([
92+
93+
'ms' => fn ($v) => $v,
94+
's' => fn ($v) => $v / 1000,
95+
'm' => fn ($v) => $v / (1000 * 60),
96+
97+
])->get($unit, 's')($this->finishedEvents[$key])
98+
: null;
99+
}
100+
101+
public function events(): array
102+
{
103+
return $this->finishedEvents;
104+
}
105+
106+
public static function startWithoutKey(): ServerTiming
107+
{
108+
return app(static::class)->start('measure');
109+
}
110+
}

src/helpers.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
use Binarcode\LaravelDeveloper\Profiling\ServerMemory;
4+
use Binarcode\LaravelDeveloper\Profiling\ServerTiming;
5+
6+
if (! function_exists('measure_memory')) {
7+
function measure_memory($callable = null, string $key = 'action', string $unit = 'mb')
8+
{
9+
/** @var ServerMemory $timing */
10+
$timing = app(ServerMemory::class);
11+
12+
if (is_callable($callable)) {
13+
$timing->setMemory($key, $callable);
14+
15+
return dd($timing->getMemory($key, $unit));
16+
}
17+
18+
$timing->measure($key);
19+
20+
return dd($timing->getMemory($key, $unit));
21+
}
22+
}
23+
24+
if (! function_exists('measure_timing')) {
25+
function measure_timing($callable = null, string $key = 'action')
26+
{
27+
/** @var ServerTiming $timing */
28+
$timing = app(ServerTiming::class);
29+
30+
if (is_callable($callable)) {
31+
$timing->setDuration($key, $callable);
32+
33+
return dd($timing->getDuration($key));
34+
}
35+
36+
$timing->measure($key);
37+
38+
return dd($timing->getDuration($key));
39+
}
40+
}

tests/Profiling/ServerMemoryTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Binarcode\LaravelDeveloper\Tests\Profiling;
4+
5+
use Binarcode\LaravelDeveloper\Profiling\ServerMemory;
6+
use Binarcode\LaravelDeveloper\Tests\TestCase;
7+
use Illuminate\Support\Collection;
8+
9+
class ServerMemoryTest extends TestCase
10+
{
11+
public function test_can_measure_memory()
12+
{
13+
/**
14+
* @var ServerMemory $memory
15+
*/
16+
$memory = ServerMemory::measure();
17+
18+
$i = Collection::times(10000, function ($i) {
19+
return $i;
20+
})->all();
21+
22+
collect($i);
23+
24+
$memory->stop();
25+
26+
$this->assertEquals(
27+
1,
28+
round($memory->getMemory())
29+
);
30+
}
31+
}

tests/Profiling/ServerTimingTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Binarcode\LaravelDeveloper\Tests\Profiling;
4+
5+
use Binarcode\LaravelDeveloper\Profiling\ServerTiming;
6+
use Binarcode\LaravelDeveloper\Tests\TestCase;
7+
8+
class ServerTimingTest extends TestCase
9+
{
10+
public function test_can_measure_timing()
11+
{
12+
/**
13+
* @var ServerTiming $timing
14+
*/
15+
$timing = ServerTiming::startWithoutKey();
16+
17+
sleep(1);
18+
19+
$timing->stopAllUnfinishedEvents();
20+
21+
$this->assertEquals(
22+
1,
23+
(int)$timing->getDuration()
24+
);
25+
}
26+
}

0 commit comments

Comments
 (0)