Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 187 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,21 @@ Or manually update `require-dev` block of `composer.json` and run `composer upda
>
> The result of the execution is printed to the console, so make sure you call the code from the console.

### Quick Start

```php
use DragonCode\Benchmark\Benchmark;

new Benchmark()
->compare(
static fn () => true,
static fn () => true
)
->toConsole();
```

### How To Use

```php
use DragonCode\Benchmark\Benchmark;

Expand Down Expand Up @@ -160,6 +175,82 @@ Result example:
+-------+----------------------+----------------------+
```

### Deviation Values

In some cases, it is necessary to test not only the functionality of the options themselves, but also to determine the
level of deviation between them. To do this, use the `deviations` method.

When you specify this method during a call, all your loops will repeat the specified number of times.
Meanwhile, the resulting table and DTO will display values from the results, where:

- `min` - minimum among all result values
- `max` - maximum among all results
- `avg` - arithmetic mean of all result values
- `total` - the total value among the values of all results
- `deviation` - deviation calculated from the mean of all results

For example:

```php
new Benchmark()
->deviations(4)
->iterations(5)
->compare(
foo: fn () => /* some code */,
bar: fn () => /* some code */,
)
->toConsole();
```

```bash
+------------------+----------------------+-----------------------+
| # | foo | bar |
+------------------+----------------------+-----------------------+
| min | 0.0011 ms - 0 bytes | 0.0009 ms - 0 bytes |
| max | 0.0111 ms - 0 bytes | 0.0082 ms - 0 bytes |
| avg | 0.00453 ms - 0 bytes | 0.002715 ms - 0 bytes |
| total | 0.0906 ms - 0 bytes | 0.0543 ms - 0 bytes |
+------------------+----------------------+-----------------------+
| order | 2 | 1 |
+------------------+----------------------+-----------------------+
| deviation time | +0.002768 | +0.000919 |
| deviation memory | 0 | 0 |
+------------------+----------------------+-----------------------+
```

#### To understand work without deviations

```php
$iterations = 10;
$result = [];

for ($i = 0; $i < $iterations; $i++) {
$result[] = /* perform some code */;
}

return $result;
```

#### To understand work with deviations

```php
$iterations = 10;
$deviations = 4;
$result = [];

for ($i = 0; $i < $deviations; $i++) {
$insideResult = [];

for ($j = 0; $j < $iterations; $j++) {
$insideResult[] = /* perform some code */;
}

$result[] = $this->performResult($insideResult);
}

return $result;
```

### Callbacks

#### Before
Expand Down Expand Up @@ -401,6 +492,35 @@ new Benchmark()
+-------+----------------------+----------------------+
```

##### Option 5: With Deviation Values

```php
new Benchmark()
->deviations(4)
->round(2)
->compare(
foo: static fn () => /* some code */,
bar: static fn () => /* some code */,
)
->toConsole();
```

```bash
+------------------+-----------------------+---------------------+
| # | 0 | 1 |
+------------------+-----------------------+---------------------+
| min | 15.68 ms - 202 bytes | 2.35 ms - 102 bytes |
| max | 112.79 ms - 209 bytes | 9.76 ms - 109 bytes |
| avg | 53.03 ms - 205 bytes | 5.94 ms - 105 bytes |
| total | 1696.81 ms - 6.42 KB | 190.17 ms - 3.30 KB |
+------------------+-----------------------+---------------------+
| order | 2 | 1 |
+------------------+-----------------------+---------------------+
| deviation time | +0.100715 | +0.114023 |
| deviation memory | 0 | 0 |
+------------------+-----------------------+---------------------+
```

#### toData

This method returns benchmark results as an array of `DragonCode\Benchmark\Data\ResultData` DTO objects.
Expand Down Expand Up @@ -457,6 +577,73 @@ array:2 [
]
```

##### With Deviation Values

When calling the benchmark with deviation calculation, the DTO will contain the `deviations` property:

```php
return new Benchmark()
->deviations()
->compare(
foo: fn () => /* some code */,
bar: fn () => /* some code */,
)
->toData();
```

```bash
array:2 [
"foo" => DragonCode\Benchmark\Data\ResultData {#23
+min: DragonCode\Benchmark\Data\MetricData {#64
+time: 0.001
+memory: 0.0
}
+max: DragonCode\Benchmark\Data\MetricData {#65
+time: 0.0036
+memory: 0.0
}
+avg: DragonCode\Benchmark\Data\MetricData {#66
+time: 0.0024209375
+memory: 0.0
}
+total: DragonCode\Benchmark\Data\MetricData {#67
+time: 0.7747
+memory: 0.0
}
+deviation: DragonCode\Benchmark\Data\DeviationData {#68
+percent: DragonCode\Benchmark\Data\MetricData {#69
+time: 0.0007048383984778
+memory: 0.0
}
}
}
"bar" => DragonCode\Benchmark\Data\ResultData {#70
+min: DragonCode\Benchmark\Data\MetricData {#71
+time: 0.001
+memory: 0.0
}
+max: DragonCode\Benchmark\Data\MetricData {#72
+time: 0.0032
+memory: 0.0
}
+avg: DragonCode\Benchmark\Data\MetricData {#73
+time: 0.00242875
+memory: 0.0
}
+total: DragonCode\Benchmark\Data\MetricData {#74
+time: 0.7772
+memory: 0.0
}
+deviation: DragonCode\Benchmark\Data\DeviationData {#75
+percent: DragonCode\Benchmark\Data\MetricData {#76
+time: 0.00061642429076895
+memory: 0.0
}
}
}
]
```

#### toAssert

This method allows you to validate benchmark results against expected values.
Expand Down
71 changes: 62 additions & 9 deletions src/Benchmark.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use DragonCode\Benchmark\Services\AssertService;
use DragonCode\Benchmark\Services\CallbacksService;
use DragonCode\Benchmark\Services\CollectorService;
use DragonCode\Benchmark\Services\DeviationService;
use DragonCode\Benchmark\Services\ResultService;
use DragonCode\Benchmark\Services\RunnerService;
use DragonCode\Benchmark\Services\ViewService;
Expand All @@ -22,13 +23,16 @@ class Benchmark
{
protected int $iterations = 100;

protected int $deviations = 1;

public function __construct(
protected RunnerService $runner = new RunnerService,
protected ViewService $view = new ViewService,
protected CallbacksService $callbacks = new CallbacksService,
protected CollectorService $collector = new CollectorService,
protected ResultService $result = new ResultService,
protected ResultTransformer $transformer = new ResultTransformer
protected ResultTransformer $transformer = new ResultTransformer,
protected DeviationService $deviation = new DeviationService,
) {}

public static function make(): static
Expand Down Expand Up @@ -86,6 +90,18 @@ public function iterations(int $count): static
return $this;
}

/**
* @param int<2, max> $count
*/
public function deviations(int $count = 2): static
{
$this->clear();

$this->deviations = max(2, abs($count));

return $this;
}

/**
* @param int<0, max>|null $precision
*/
Expand All @@ -98,6 +114,8 @@ public function round(?int $precision): static

public function compare(array|Closure ...$callbacks): static
{
$this->clear();

$this->callbacks->compare(...$callbacks);

return $this;
Expand All @@ -108,11 +126,11 @@ public function compare(array|Closure ...$callbacks): static
*/
public function toData(): array
{
$this->performCallbacks();
if (! $this->result->has()) {
$this->perform();
}

return $this->result->get(
$this->collector->all()
);
return $this->mapResult();
}

public function toConsole(): static
Expand All @@ -133,10 +151,22 @@ public function toAssert(): AssertService
);
}

protected function performCallbacks(): void
protected function perform(): void
{
$this->clear();
$this->deviations === 1
? $this->performCompare()
: $this->performDeviation();
}

protected function mapResult(): array
{
return $this->result->get(
$this->collector->all()
);
}

protected function performCompare(): void
{
$callbacks = $this->callbacks->compare;

$this->withProgress(
Expand All @@ -145,6 +175,29 @@ protected function performCallbacks(): void
);
}

protected function performDeviation(): void
{
$results = [];

$callbacks = $this->callbacks->compare;

$this->withProgress(function (ProgressBarView $bar) use (&$results, $callbacks) {
for ($i = 1; $i <= $this->deviations; $i++) {
$this->clear();

$this->chunks($callbacks, $bar);

$results[] = $this->mapResult();
}
}, $this->steps($callbacks, $this->deviations));

$this->clear();

$this->result->force(
$this->deviation->calculate($results)
);
}

protected function withProgress(Closure $callback, int $total): void
{
$this->view->emptyLine();
Expand All @@ -157,9 +210,9 @@ protected function withProgress(Closure $callback, int $total): void
$this->view->emptyLine(2);
}

protected function steps(array $callbacks): int
protected function steps(array $callbacks, int $multiplier = 1): int
{
return count($callbacks) * $this->iterations;
return count($callbacks) * $this->iterations * $multiplier;
}

protected function chunks(array $callbacks, ProgressBarView $progressBar): void
Expand Down
12 changes: 12 additions & 0 deletions src/Data/DeviationData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace DragonCode\Benchmark\Data;

readonly class DeviationData
{
public function __construct(
public MetricData $percent,
) {}
}
1 change: 1 addition & 0 deletions src/Data/ResultData.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ public function __construct(
public MetricData $max,
public MetricData $avg,
public MetricData $total,
public ?DeviationData $deviation = null,
) {}
}
Loading
Loading