dragon-code/benchmark
Lightweight PHP benchmarking helper to compare execution speed of multiple callbacks. Run named tests, repeat iterations, trim outliers for cleaner averages, and print results (min/max/avg/total + memory) to the console. Includes bench() helper and Benchmark class.
Installation:
composer require dragon-code/benchmark --dev
Add to composer.json under require-dev to avoid production bloat.
First Benchmark:
use function DragonCode\Benchmark\bench;
bench()
->compare(
'array_map': fn () => array_map('strtoupper', ['a', 'b', 'c']),
'foreach': fn () => array_reduce(['a', 'b', 'c'], fn ($carry, $item) => $carry .= strtoupper($item), '')
)
->toConsole();
Key Files to Explore:
vendor/dragon-code/benchmark/src/ for core logictests/ for usage patterns and edge casesbench()
->iterations(50)
->compare(
'eloquent': fn () => Model::where('active', true)->get(),
'query': fn () => DB::select('SELECT * FROM models WHERE active = ?', [true])
)
->toConsole();
// Compare two query approaches
bench()
->warmup(2)
->compare(
'eager': fn () => Model::with('relations')->find(1),
'lazy': fn () => Model::find(1)->load('relations')
)
->toConsole();
// In PHPUnit tests
public function testPerformanceRegression()
{
$result = bench()
->compare(
'old': fn () => $this->oldAlgorithm(),
'new': fn () => $this->newAlgorithm()
)
->toData();
$this->assertLessThan(1.5, $result['new']['avg']['time'] / $result['old']['avg']['time']);
}
// Benchmark Blade vs. inline PHP
bench()
->beforeEach(fn ($name) => View::share('data', $this->generateTestData()))
->compare(
'blade': fn () => view('template')->render(),
'php': fn () => $this->renderInlineTemplate()
)
->toConsole();
// Warmup + data setup
bench()
->warmup(3)
->beforeEach(fn ($name, $iteration) => $this->setupTestData($iteration))
->compare(
'collection': fn ($data) => collect($data)->sum('value'),
'array': fn ($data) => array_reduce($data, fn($carry, $item) => $carry + $item['value'], 0))
)
->toConsole();
// In a dedicated benchmark test file
bench()
->snapshots(__DIR__.'/../.benchmarks')
->compare(
'cache': fn () => Cache::get('key'),
'db': fn () => DB::table('cache')->where('key', 'key')->first()
)
->toAssert()
->toBeRegressionTime(max: 10)
->toBeRegressionMemory(max: 5);
Cold Start Bias:
->warmup(2) for accurate resultsMemory Leak Misinterpretation:
max memory might spike due to garbage collection timing->deviations(3) to smooth out variationsSnapshot Directory Conflicts:
.benchmarks/->snapshots(__DIR__.'/../../.benchmarks')
Callback Parameter Confusion:
beforeEach return value is passed to main callback->beforeEach(fn ($name, $iteration) => $this->prepareData())
->compare(
fn ($data) => $this->process($data), // $data comes from beforeEach
)
Progress Bar Interference:
->disableProgressBar()
Inspect Raw Data:
$data = bench()->compare(...)->toData();
dd($data['callback_name']['avg']['time']); // Debug specific metric
Visualize Deviations:
bench()
->deviations(5)
->round(4)
->compare(...)
->toConsole();
Isolate Variables:
// Compare with fixed inputs
$testData = $this->generateConsistentData();
bench()
->beforeEach(fn ($name) => $this->resetState())
->compare(
fn () => $this->methodA($testData),
fn () => $this->methodB($testData),
);
Custom Output Formatting:
// Extend Benchmark class to add CSV output
class CSVBenchmark extends Benchmark {
public function toCSV(): string {
// Implement custom CSV generation
}
}
Database-Specific Warmup:
bench()
->warmup(5)
->before(fn () => DB::connection()->getPdo()->exec('SET SESSION query_cache_type = ON'))
->compare(...);
Parallel Benchmarking (Laravel 10+):
// Use Laravel's task scheduling for parallel runs
Dispatch::later(now()->addMinutes(1), function () {
bench()->compare(...)->toConsole();
});
Memory Profiling Integration:
// Combine with Xdebug
bench()
->beforeEach(fn () => xdebug_start_trace())
->afterEach(fn () => xdebug_stop_trace())
->compare(...);
Environment-Specific Config:
// In a service provider
Benchmark::macro('ciConfig', function() {
return $this->disableProgressBar()->iterations(20);
});
// Usage
bench()->ciConfig()->compare(...);
Iteration Thresholds:
->iterations(15)->deviations(2)
Snapshot File Naming:
->snapshots(__DIR__.'/../../benchmarks')
Static Closure Caveats:
->compare(
[$this, 'method'], // Instead of fn() => $this->method()
)
Memory Measurement Limitations:
memory_get_peak_usage(true) // In bytes
How can I help you explore Laravel packages today?