Skip to content

Instantly share code, notes, and snippets.

@klkvsk
Created December 12, 2020 14:38
Show Gist options
  • Save klkvsk/98eb1c3b1cc35b77ebd86bdc1b8820a3 to your computer and use it in GitHub Desktop.
Save klkvsk/98eb1c3b1cc35b77ebd86bdc1b8820a3 to your computer and use it in GitHub Desktop.
php-enum property collection benchmarks
<?php
require __DIR__ . '/../vendor/autoload.php';
$classes = require __DIR__ . '/classes.php';
return function (callable $propertyReader) use ($classes) {
$t1 = microtime(true);
$numFiles = 0;
$numProps = 0;
foreach ($classes as $className)
{
$numFiles++;
$fields = $propertyReader($className);
$numProps += count($fields);
}
$t2 = microtime(true);
echo "Read $numFiles files, $numProps properties total\n";
echo "Total time consumed: ";
echo number_format(1000 * ($t2 - $t1), 2, '.', ' ') . " ms\n";
};
<?php
$numFiles = 1000;
$numFieldGroups = 2;
$numFieldsInGroup = 5;
function cleanup($dir) {
/** @var SplFileInfo[] $files */
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS));
foreach ($files as $file) {
unlink($file->getPathname());
}
}
$generatedDir = __DIR__ . '/generated';
if (is_dir($generatedDir)) {
cleanup($generatedDir);
} else {
mkdir($generatedDir);
}
$classes = [];
for ($i = 0; $i < $numFiles; $i++) {
$className = 'GeneratedEnum' . $i;
$classCode = "<?php" . PHP_EOL . PHP_EOL;
$classCode .= 'use PaLabs\Enum\Enum;' . PHP_EOL . PHP_EOL . PHP_EOL;
$classCode .= "class $className extends Enum" . PHP_EOL;
$classCode .= "{" . PHP_EOL;
for ($g = 0; $g < $numFieldGroups; $g++) {
$classCode .= " public static $className";
for ($f = 0; $f < $numFieldsInGroup; $f++) {
$classCode .= sprintf(" \$VALUE_%02X_%02X", $g, $f);
$classCode .= ($f+1 == $numFieldsInGroup) ? ';' : ',';
}
$classCode .= PHP_EOL;
}
$classCode .= "}" . PHP_EOL;
$filename = "$generatedDir/$className.php";
file_put_contents($filename, $classCode);
$classes[$filename] = $className;
}
$autoload = '<?php' . PHP_EOL;
foreach ($classes as $filename => $className) {
$autoload .= "require '$filename';" . PHP_EOL;
}
$autoload .= PHP_EOL;
$autoload .= 'return [' . PHP_EOL;
foreach ($classes as $filename => $className) {
$autoload .= ' "' . $filename . '" => ' . $className . '::class,' . PHP_EOL;
}
$autoload .= '];' . PHP_EOL;
file_put_contents(__DIR__ . '/classes.php', $autoload);
<?php
$bench = require 'bench.php';
$bench(function ($className)
{
$class = new ReflectionClass($className);
$shortClass = $class->getShortName();
$fileContent = file_get_contents($class->getFileName());
// use regexp to extract public static properties with type of enum class name
// [\s]+? - any space (including \n symbol), ? - lazy qualifier
$pattern = "/public[\s]+?static[\s]+?".$shortClass."[\s]+?([^;]+?);/i";
preg_match_all($pattern, $fileContent, $matches);
if(count($matches) < 2) {
return [];
}
$allFields = [];
foreach($matches[1] as $fieldsStr) {
$fields = explode(',', $fieldsStr);
$fields = array_map('trim', $fields);
$fields = array_map(
fn(string $fieldName) => substr($fieldName, 1),
$fields
);
$allFields = array_merge($allFields, $fields);
}
return $allFields;
});
<?php
$bench = require 'bench.php';
$bench(function ($className)
{
$enumValues = [];
$classHierarchy = [];
$class = new ReflectionClass($className);
$properties = $class->getProperties(\ReflectionProperty::IS_STATIC | \ReflectionProperty::IS_PUBLIC);
foreach ($properties as $property) {
$type = $property->getType();
if (!$type instanceof \ReflectionNamedType) {
continue;
}
$matched = false;
if ($type->getName() === $class->getName()) {
$matched = true;
} else if ($type->getName() === 'self' || $type->getName() == 'parent') {
$matched = true;
} else if (class_exists($type->getName()) && is_subclass_of($class->getName(), $type->getName())) {
$matched = true;
}
if ($matched) {
$declClassName = $property->getDeclaringClass()->getName();
if (!isset($enumValues[$declClassName])) {
$enumValues[$declClassName] = [];
$classHierarchy[] = $declClassName;
}
$enumValues[$declClassName][] = $property->getName();
}
}
$list = [];
foreach (array_reverse($classHierarchy) as $declClassName) {
foreach ($enumValues[$declClassName] as $value) {
$list[] = [ $declClassName, $value ];
}
}
return $list;
});
## 1000 files, 10 values in each
-- file parsing reader --
Read 1000 files, 10000 properties total
Total time consumed: 102.24 ms
-- reflection reader --
Read 1000 files, 10000 properties total
Total time consumed: 45.46 ms
## 1000 files, 100 values in each
-- file parsing reader --
Read 1000 files, 100000 properties total
Total time consumed: 319.40 ms
-- reflection reader --
Read 1000 files, 100000 properties total
Total time consumed: 397.64 ms
#!/usr/bin/env bash
mkdir generated
echo "generating enums"
php create-enums.php
echo ""
echo "-- file parsing reader --"
php read-with-file-parsing.php
echo ""
echo "-- reflection reader --"
php read-with-reflection-only.php
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment