To get the latest version of Benchmark, simply require the project using Composer:
composer require dragon-code/benchmark --devOr manually update require-dev block of composer.json and run composer update console command:
{
"require-dev": {
"dragon-code/benchmark": "^4.0"
}
}Note
The result of the execution is printed to the console, so make sure you call the code from the console.
use DragonCode\Benchmark\Benchmark;
// Array without named keys
new Benchmark()->compare([
fn () => /* some code */,
fn () => /* some code */,
])->toConsole();
// Array with named keys
new Benchmark()->compare([
'foo' => fn () => /* some code */,
'bar' => fn () => /* some code */,
])->toConsole();
// Callbacks without named parameters
new Benchmark()->compare(
fn () => /* some code */,
fn () => /* some code */,
)->toConsole();
// Callbacks with named parameters
new Benchmark()->compare(
foo: fn () => /* some code */,
bar: fn () => /* some code */,
)->toConsole();Example output with named keys:
+-------+-------------------------+-------------------------+
| # | foo | bar |
+-------+-------------------------+-------------------------+
| min | 14.3472 ms - 0 bytes | 14.3657 ms - 0 bytes |
| max | 15.7684 ms - 0 bytes | 15.7249 ms - 0 bytes |
| avg | 15.0967475 ms - 0 bytes | 14.9846725 ms - 0 bytes |
| total | 1207.7398 ms - 0 bytes | 1198.7738 ms - 0 bytes |
+-------+-------------------------+-------------------------+
| order | 2 | 1 |
+-------+-------------------------+-------------------------+When measuring the average value among the results, when more than 9 iterations are used, the final data is filtered by peak values. The calculation of the 10% of the lowest and 10% of the highest values is excluded from the total result, thus the final data becomes cleaner and less dependent on any external factors.
By default, the benchmark performs 100 iterations per callback, but you can change this number by calling
the iterations method:
use DragonCode\Benchmark\Benchmark;
new Benchmark()
->iterations(5)
->compare([
'foo' => fn () => /* some code */,
'bar' => fn () => /* some code */,
])
->toConsole();If a negative value is passed, its absolute value will be used.
For example:
use DragonCode\Benchmark\Benchmark;
new Benchmark()
->iterations(-20) // Will result in 20 iterations
// ...You can also get the number of the current execution iteration from the input parameter:
use DragonCode\Benchmark\Benchmark;
new Benchmark()
->iterations(5)
->compare(
fn (int $iteration) => /* some code */,
fn (int $iteration) => /* some code */,
)
->toConsole();By default, the script does not round measurement results, but you can specify the number of decimal places to which rounding can be performed.
This method only affects the console output (the toConsole method).
For example:
use DragonCode\Benchmark\Benchmark;
new Benchmark()
->round(2)
->compare([
'foo' => fn () => /* some code */,
'bar' => fn () => /* some code */,
])
->toConsole();Result example:
+-------+----------------------+----------------------+
| # | foo | bar |
+-------+----------------------+----------------------+
| min | 14.58 ms - 0 bytes | 14.38 ms - 0 bytes |
| max | 15.55 ms - 0 bytes | 15.71 ms - 0 bytes |
| avg | 15.01 ms - 0 bytes | 15.1 ms - 0 bytes |
| total | 1201.09 ms - 0 bytes | 1207.76 ms - 0 bytes |
+-------+----------------------+----------------------+
| order | 1 | 2 |
+-------+----------------------+----------------------+In some cases, you may need to perform certain actions before running the benchmark loop.
You can do this by calling the before method with a callback.
use DragonCode\Benchmark\Benchmark;
new Benchmark()
->before(fn () => /* some code */)
->compare(
fn () => /* some code */,
fn () => /* some code */,
)
->toConsole();The loop name is passed to the callback as a parameter. You can use this information if needed.
use DragonCode\Benchmark\Benchmark;
new Benchmark()
->before(fn (int|string $name) => /* some code */)
->compare(
fn () => /* some code */,
fn () => /* some code */,
)
->toConsole();In some cases, you may need to perform certain actions before each benchmark iteration.
You can do this by calling the beforeEach method with a callback.
use DragonCode\Benchmark\Benchmark;
new Benchmark()
->beforeEach(fn () => /* some code */)
->compare(
fn () => /* some code */,
fn () => /* some code */,
)
->toConsole();The loop name and iteration number are passed to the callback as parameters.
Additionally, the result of the beforeEach callback is passed to the compare callback itself.
You can use this information if needed.
use DragonCode\Benchmark\Benchmark;
new Benchmark()
->beforeEach(fn (int|string $name, int $iteration) => /* some code */)
->compare(
fn (mixed $before) => /* some code */,
fn (mixed $before) => /* some code */,
)
->toConsole();In some cases, you may need to perform certain actions after the benchmark loop has completed.
You can do this by calling the after method with a callback.
use DragonCode\Benchmark\Benchmark;
new Benchmark()
->after(fn () => /* some code */)
->compare(
fn () => /* some code */,
fn () => /* some code */,
)
->toConsole();The loop name is passed to the callback as a parameter. You can use this information if needed.
use DragonCode\Benchmark\Benchmark;
new Benchmark()
->after(fn (int|string $name) => /* some code */)
->compare(
fn () => /* some code */,
fn () => /* some code */,
)
->toConsole();In some cases, you may need to perform certain actions after each benchmark iteration.
You can do this by calling the afterEach method with a callback.
use DragonCode\Benchmark\Benchmark;
new Benchmark()
->afterEach(fn () => /* some code */)
->compare(
fn () => /* some code */,
fn () => /* some code */,
)
->toConsole();The loop name and iteration number are passed to the callback as parameters. You can use this information if needed.
use DragonCode\Benchmark\Benchmark;
new Benchmark()
->afterEach(fn (int|string $name, int $iteration) => /* some code */)
->compare(
fn () => /* some code */,
fn () => /* some code */,
)
->toConsole();Use one of the following methods to obtain benchmark results.
This method outputs the benchmark results to the console.
new Benchmark()
->round(2)
->compare([
'foo' => static fn () => /* some code */,
'bar' => static fn () => /* some code */,
])
->toConsole();+-------+---------------------+----------------------+
| # | foo | bar |
+-------+---------------------+----------------------+
| min | 14.56 ms - 0 bytes | 14.62 ms - 0 bytes |
| max | 15.85 ms - 0 bytes | 15.65 ms - 0 bytes |
| avg | 15.08 ms - 0 bytes | 15.12 ms - 0 bytes |
| total | 1206.7 ms - 0 bytes | 1209.44 ms - 0 bytes |
+-------+---------------------+----------------------+
| order | 1 | 2 |
+-------+---------------------+----------------------+new Benchmark()
->round(2)
->compare([
static fn () => /* some code */,
static fn () => /* some code */,
])
->toConsole();+-------+----------------------+----------------------+
| # | 0 | 1 |
+-------+----------------------+----------------------+
| min | 14.52 ms - 0 bytes | 14.42 ms - 0 bytes |
| max | 15.78 ms - 0 bytes | 15.7 ms - 0 bytes |
| avg | 15.09 ms - 0 bytes | 15.01 ms - 0 bytes |
| total | 1207.56 ms - 0 bytes | 1200.55 ms - 0 bytes |
+-------+----------------------+----------------------+
| order | 2 | 1 |
+-------+----------------------+----------------------+new Benchmark()
->round(2)
->compare(
static fn () => /* some code */,
static fn () => /* some code */,
)
->toConsole();+-------+----------------------+----------------------+
| # | 0 | 1 |
+-------+----------------------+----------------------+
| min | 14.52 ms - 0 bytes | 14.56 ms - 0 bytes |
| max | 15.68 ms - 0 bytes | 15.61 ms - 0 bytes |
| avg | 15.1 ms - 0 bytes | 15.04 ms - 0 bytes |
| total | 1207.73 ms - 0 bytes | 1203.17 ms - 0 bytes |
+-------+----------------------+----------------------+
| order | 2 | 1 |
+-------+----------------------+----------------------+new Benchmark()
->round(2)
->compare(
foo: static fn () => /* some code */,
bar: static fn () => /* some code */,
)
->toConsole();+-------+----------------------+----------------------+
| # | foo | bar |
+-------+----------------------+----------------------+
| min | 14.68 ms - 0 bytes | 14.56 ms - 0 bytes |
| max | 15.69 ms - 0 bytes | 15.64 ms - 0 bytes |
| avg | 15.13 ms - 0 bytes | 15.07 ms - 0 bytes |
| total | 1210.38 ms - 0 bytes | 1205.26 ms - 0 bytes |
+-------+----------------------+----------------------+
| order | 2 | 1 |
+-------+----------------------+----------------------+This method returns benchmark results as an array of DragonCode\Benchmark\Data\ResultData DTO objects.
You can use it in your application for your own purposes.
return new Benchmark()
->compare(
foo: fn () => /* some code */,
bar: fn () => /* some code */,
)
->toData();array:2 [
"foo" => DragonCode\Benchmark\Data\ResultData {#17
+min: DragonCode\Benchmark\Data\MetricData {#19
+time: 14.6123
+memory: 0.0
}
+max: DragonCode\Benchmark\Data\MetricData {#20
+time: 15.7372
+memory: 0.0
}
+avg: DragonCode\Benchmark\Data\MetricData {#21
+time: 15.12268875
+memory: 0.0
}
+total: DragonCode\Benchmark\Data\MetricData {#22
+time: 1209.8151
+memory: 0.0
}
}
"bar" => DragonCode\Benchmark\Data\ResultData {#23
+min: DragonCode\Benchmark\Data\MetricData {#24
+time: 14.3369
+memory: 0.0
}
+max: DragonCode\Benchmark\Data\MetricData {#25
+time: 15.8259
+memory: 0.0
}
+avg: DragonCode\Benchmark\Data\MetricData {#26
+time: 15.10940625
+memory: 0.0
}
+total: DragonCode\Benchmark\Data\MetricData {#27
+time: 1208.7525
+memory: 0.0
}
}
]This method allows you to validate benchmark results against expected values.
use DragonCode\Benchmark\Benchmark;
new Benchmark()
->compare(/* ... */)
->toAssert()
->toBeMinTime(0.5, 3) // between 0.5 and 3 ms
->toBeMaxTime(0.5, 3) // between 0.5 and 3 ms
->toBeAvgTime(0.5, 3) // between 0.5 and 3 ms
->toBeTotalTime(0.5, 9) // between 0.5 and 9 ms
->toBeMinMemory(0, 1024) // between 0 and 1024 bytes
->toBeMaxMemory(0, 1024) // between 0 and 1024 bytes
->toBeAvgMemory(0, 1024) // between 0 and 1024 bytes
->toBeTotalMemory(0, 4096); // between 0 and 4096 bytesYou can also use a single value:
use DragonCode\Benchmark\Benchmark;
new Benchmark()
->compare(/* ... */)
->toAssert()
->toBeMinTime(0.5) // time must be greater than or equal to 0.5 ms
->toBeMaxTime(0.5) // time must be greater than or equal to 0.5 ms
->toBeAvgTime(0.5) // time must be greater than or equal to 0.5 ms
->toBeTotalTime(0.5) // time must be greater than or equal to 0.5 ms
->toBeMinMemory(till: 1024) // the memory footprint should not exceed 1024 bytes
->toBeMaxMemory(till: 1024) // the memory footprint should not exceed 1024 bytes
->toBeAvgMemory(till: 1024) // the memory footprint should not exceed 1024 bytes
->toBeTotalMemory(till: 4096); // the memory footprint should not exceed 4096 bytesThis package is licensed under the MIT License.