Skip to content

Commit

Permalink
Added benchmark operation
Browse files Browse the repository at this point in the history
  • Loading branch information
Emre Çalışkan committed Jan 1, 2024
1 parent 73cce9a commit 6a236bf
Show file tree
Hide file tree
Showing 11 changed files with 411 additions and 72 deletions.
221 changes: 221 additions & 0 deletions app/Console/Commands/BenchmarkCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
<?php

namespace App\Console\Commands;

use App\Enums\EndpointEnum;
use App\Enums\ServerEnum;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Facades\Storage;

class BenchmarkCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:benchmark';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Run Benchmark';

/**
* Execute the console command.
*/
public function handle()
{
foreach (ServerEnum::cases() as $serverEnum) {
foreach (EndpointEnum::cases() as $endpointEnum) {
$this->info($serverEnum->getBenchmarkCommand($endpointEnum));

$process = Process::start($serverEnum->getBenchmarkCommand($endpointEnum));

$statistics = [];
usleep(1000000 - now()->microsecond);
while ($process->running()) {
if (isset($statistics[now()->toAtomString()])) {
usleep(1000000 - now()->microsecond);

continue;
}
$statistics[now()->toAtomString()] = Process::start($serverEnum->getStatsCommand());
}

$benchmarkOutput = $process->wait()->output();

foreach ($statistics as $key => $statistic) {
$statistics[$key] = json_decode($statistic->wait()->output(), true);
}

$explodedBenchmarkOutput = explode("\nJSON Output:\n", $benchmarkOutput);

Storage::disk('benchmark')->put(
$endpointEnum->getFileName($serverEnum)->append('-wrk.md'),
$explodedBenchmarkOutput[0]
);

Storage::disk('benchmark')->put(
$endpointEnum->getFileName($serverEnum)->append('-wrk.json'),
json_encode(json_decode($explodedBenchmarkOutput[1]), JSON_PRETTY_PRINT)
);

Storage::disk('benchmark')->put(
$endpointEnum->getFileName($serverEnum)->append('-statistics.json'),
json_encode($statistics, JSON_PRETTY_PRINT)
);
}
}

$this->generateTables();

return true;
}

protected function arrayToCsv(string $filename, array $data): void
{
$csv = fopen(storage_path('table/'.$filename.'.csv'), 'w');
foreach ($data as $datum) {
fputcsv($csv, $datum, ',', '"');
}
fclose($csv);
}

protected function getBenchmarkData($serverEnum, $endpointEnum): Collection
{
return collect(json_decode(Storage::disk('benchmark')->get($endpointEnum->getFileName($serverEnum)->append('-wrk.json')), true));
}

protected function getStatisticsData($serverEnum, $endpointEnum): Collection
{
return collect(json_decode(Storage::disk('benchmark')->get($endpointEnum->getFileName($serverEnum)->append('-statistics.json')), true));
}

public function generateTables(): void
{
$this->generateRequestsTable();
$this->generateRequestPerSecondTable();
$this->generateTransferPerSecondTable();
$this->generateLatencyDistributionTable();
$this->generateCpuUsageTable();
$this->generateMemoryUsageTable();
}

public function generateRequestPerSecondTable()
{
$data = [['Server']];

foreach (EndpointEnum::cases() as $endpointEnum) {
$data[0][] = $endpointEnum->getTitle();
}

foreach (ServerEnum::cases() as $serverEnum) {
$data[$serverEnum->name][] = $serverEnum->getTitle();
foreach (EndpointEnum::cases() as $endpointEnum) {
$data[$serverEnum->name][] = $this->getBenchmarkData($serverEnum, $endpointEnum)->get('requests_per_sec');
}
}

$this->arrayToCsv('request-per-second', $data);
}

public function generateRequestsTable()
{
$data = [['Server']];

foreach (EndpointEnum::cases() as $endpointEnum) {
$data[0][] = $endpointEnum->getTitle();
}

foreach (ServerEnum::cases() as $serverEnum) {
$data[$serverEnum->name][] = $serverEnum->getTitle();
foreach (EndpointEnum::cases() as $endpointEnum) {
$data[$serverEnum->name][] = $this->getBenchmarkData($serverEnum, $endpointEnum)->get('requests');
}
}

$this->arrayToCsv('requests', $data);
}

public function generateTransferPerSecondTable()
{
$data = [['Server']];

foreach (EndpointEnum::cases() as $endpointEnum) {
$data[0][] = $endpointEnum->getTitle();
}

foreach (ServerEnum::cases() as $serverEnum) {
$data[$serverEnum->name][] = $serverEnum->getTitle();
foreach (EndpointEnum::cases() as $endpointEnum) {
$data[$serverEnum->name][] = $this->getBenchmarkData($serverEnum, $endpointEnum)->get('bytes_transfer_per_sec') / 1024;
}
}

$this->arrayToCsv('transfer-per-second', $data);
}

public function generateLatencyDistributionTable()
{
foreach (EndpointEnum::cases() as $endpointEnum) {
$data = [['Server', '50%', '75%', '90%', '99%', '99.99%']];
foreach (ServerEnum::cases() as $serverEnum) {
$data[$serverEnum->name][] = $serverEnum->getTitle();

foreach ($this->getBenchmarkData($serverEnum, $endpointEnum)->get('latency_distributions') as $latencyDistribution) {
$data[$serverEnum->name][] = $latencyDistribution['latency_in_microseconds'] / 1000;
}
}
$this->arrayToCsv('latency-distribution-'.$endpointEnum->getKebabCase(), $data);
}
}

public function generateCpuUsageTable()
{
foreach (EndpointEnum::cases() as $endpointEnum) {
$data = [[
'Server',
...array_map(
fn ($second) => str($second)->padLeft(2, 0)->prepend('00:'),
range(1, $endpointEnum->getBenchmarkDuration())
),
]];
foreach (ServerEnum::cases() as $serverEnum) {
$data[$serverEnum->name] = [
$serverEnum->getTitle(),
...$this->getStatisticsData($serverEnum, $endpointEnum)
->pluck('CPUPerc')
->map(fn ($percentage) => str_replace('%', '', $percentage)),
];
}
$this->arrayToCsv('cpu-usage-'.$endpointEnum->getKebabCase(), $data);
}
}

public function generateMemoryUsageTable()
{
foreach (EndpointEnum::cases() as $endpointEnum) {
$data = [[
'Server',
...array_map(
fn ($second) => str($second)->padLeft(2, 0)->prepend('00:'),
range(1, $endpointEnum->getBenchmarkDuration())
),
]];
foreach (ServerEnum::cases() as $serverEnum) {
$data[$serverEnum->name] = [
$serverEnum->getTitle(),
...$this->getStatisticsData($serverEnum, $endpointEnum)
->pluck('MemUsage')
->map(fn ($usage) => explode('MiB', $usage)[0]),
];
}
$this->arrayToCsv('memory-usage-'.$endpointEnum->getKebabCase(), $data);
}
}
}
43 changes: 43 additions & 0 deletions app/Enums/EndpointEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace App\Enums;

use Illuminate\Support\Stringable;

enum EndpointEnum
{
case HealthCheck;
case Static;
case HttpRequest;

public function getRoute(): string
{
return match ($this) {
self::HealthCheck => route('health-check', [], false),
self::Static => route('static', [], false),
self::HttpRequest => route('http-request', [], false),
};
}

public function getFileName(ServerEnum $serverEnum): Stringable
{
return str($serverEnum->name.$this->name)->replace('PHP', 'Php')->kebab();
}

public function getBenchmarkDuration(): int
{
return match ($this) {
default => 30,
};
}

public function getKebabCase(): string
{
return str($this->name)->kebab();
}

public function getTitle(): string
{
return str($this->name)->snake(' ')->title();
}
}
36 changes: 36 additions & 0 deletions app/Enums/ServerEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace App\Enums;

enum ServerEnum
{
case OpenSwoole;
case Swoole;
case RoadRunner;
case FrankenPHP;

public function getPort(): int
{
return match ($this) {
self::OpenSwoole => 9801,
self::Swoole => 9802,
self::RoadRunner => 9803,
self::FrankenPHP => 9804,
};
}

public function getBenchmarkCommand(EndpointEnum $endpointEnum): string
{
return 'wrk -t8 -c16 -d'.$endpointEnum->getBenchmarkDuration().'s -s json.lua --latency http://127.0.0.1:'.$this->getPort().$endpointEnum->getRoute();
}

public function getStatsCommand(): string
{
return 'docker stats '.strtolower($this->name).' --format=json --no-stream';
}

public function getTitle(): string
{
return $this->name;
}
}
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi"
]
],
"format": "vendor/bin/pint"
},
"extra": {
"laravel": {
Expand Down
Loading

0 comments on commit 6a236bf

Please sign in to comment.