bin/bench

Summary

Maintainability
Test Coverage
#!/usr/bin/env php
<?php

declare(strict_types=1);

use Smuuf\Primi\Cli\Term;
use Smuuf\Primi\Helpers\Colors;

require __DIR__ . '/../vendor/autoload.php';
chdir(__DIR__ . '/..');

/**
 * Runs a shell command, prints out the first line of output as it goes, then
 * don't show the rest, but return the rest of the output as a string.
 */
function run_shell_cmd(string $cmd): string {

    $output = [];
    $firstLine = true;
    $proc = popen($cmd, 'r');
    while (!feof($proc)) {

        $buffer = fread($proc, 4096);

        if ($firstLine) {

            // If were gathering the first line of output and there's a newline
            // character in the current buffer, split it, echo the first part
            // (which is still the first line) and gather the second part into
            // our output buffer (which we will want to return from this
            // function).
            if (($newLinePos = mb_strpos($buffer, "\n")) !== false) {
                //echo "█D";
                echo mb_substr($buffer, 0, $newLinePos);
                $output[] = mb_substr($buffer, $newLinePos + 1);
                $firstLine = false;
            } else {
                //echo "█B";
                echo $buffer;
            }

            @flush();

        } else {
            //echo "█O";
            $output[] = $buffer;
        }

    }

    pclose($proc);
    return trim(implode('', $output));

}


function show_results(array $data, string $standardKey): void {

    $standardAvg = array_sum($data[$standardKey]) / count($data[$standardKey]);
    foreach ($data as $key => $times) {

        $avgTime = round(array_sum($times) / count($times), 6);
        $paddedKey = str_pad("$key:", 10);

        $slowerInfo = '';
        if ($key !== $standardKey) {
            $slower = round($avgTime / $standardAvg, 2);
            $slowerInfo = "({$slower}x slower)";
        }

        echo "  $paddedKey $avgTime s $slowerInfo\n";

    }

}

function color(string $text, string $color, bool $noNewline = false): void {
    $out = Colors::get("{{$color}}█{_} $text");

    if ($noNewline) {
        echo $out;
        return;
    }

    echo Term::line($out);
}

function title(string $text, bool $noNewline = false): void {
    color($text, color: 'yellow', noNewline: $noNewline);
}

function info(string $text, bool $noNewline = false): void {
    color($text, color: 'blue', noNewline: $noNewline);
}

function shell(string $cmd, bool $print = false): string {

    $result = shell_exec($cmd);
    if ($print) {
        echo $result;
    }

    return $result ?? '';

}

function run_benchmark(string $name, string $command, int $times = 1): array {

    $results = [];
    foreach (range(1, $times) as $index) {
        $index = $times !== 1 ? "[$index] " : '';
        info("{yellow}$name{_} {darkgrey}$index{_}... ", noNewline: true);

        // Output of the bench program (except the first line) looks like this:
        // 1. line - self-measured time.
        // 2. line - self-measured memory peak.
        $output = run_shell_cmd($command);
        [$time, $mempeak] = explode("\n", $output);

        echo "\n";
        echo Term::line("  Took $time s");
        echo Term::line("  Mempeak $mempeak MB");
        $results[] = $time;

    }

    return $results;

}

$interpreter = $argv[1] ?? 'php';

info("Using interpreter: $interpreter");
shell("$interpreter --version | head -n1", print: true);

const ITERATIONS = 3;

$perfPhpPath = './tests/bench/perf_bench_php.php';
$perfPythonPath = './tests/bench/perf_bench_python.py';
$perfPrimiPath = './tests/bench/perf_bench_primi.primi';

$results = [
    'php' => [],
    'python' => [],
    'primi' => [],
];

$results['php'] = run_benchmark('PHP', "$interpreter $perfPhpPath", times: 3);

if (shell("command -v python")) {
    $results['python'] = run_benchmark('Python', "python $perfPythonPath", times: 3);
} else {
    info("Python not available.");
}

$results['primi'] = run_benchmark('Primi', "$interpreter ./primi $perfPrimiPath", times: 3);

title("Results:");
show_results($results, 'php');