mirror of
https://github.com/chillerlan/php-qrcode.git
synced 2026-08-24 16:27:54 +00:00
✨ cheap performance tests
This commit is contained in:
@@ -18,3 +18,4 @@ phpcs.xml
|
||||
phpdoc.xml
|
||||
phpmd.xml
|
||||
phpunit.xml
|
||||
tests/Performance/*.json
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<testsuites>
|
||||
<testsuite name="php-qrcode test suite">
|
||||
<directory suffix=".php">./tests/</directory>
|
||||
<exclude>./tests/Performance</exclude>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<coverage processUncoveredFiles="true">
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* Class PerformanceTest
|
||||
*
|
||||
* @created 16.10.2023
|
||||
* @author smiley <smiley@chillerlan.net>
|
||||
* @copyright 2023 smiley
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
namespace chillerlan\QRCodeTest\Performance;
|
||||
|
||||
use Closure;
|
||||
use function hrtime;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class PerformanceTest{
|
||||
|
||||
protected int $runs;
|
||||
protected int $total = 0;
|
||||
|
||||
public function __construct(int $runs = 1000){
|
||||
$this->runs = $runs;
|
||||
}
|
||||
|
||||
public function run(Closure $subject):self{
|
||||
$this->total = 0;
|
||||
|
||||
for($i = 0; $i < $this->runs; $i++){
|
||||
$start = hrtime(true);
|
||||
|
||||
$subject();
|
||||
|
||||
$end = hrtime(true);
|
||||
|
||||
$this->total += ($end - $start);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getResult():float{
|
||||
return ($this->total / $this->runs / 1000000);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* Tests the performance of the mask pattern penalty testing
|
||||
*
|
||||
* @created 18.10.2023
|
||||
* @author smiley <smiley@chillerlan.net>
|
||||
* @copyright 2023 smiley
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
namespace chillerlan\QRCodeTest\Performance;
|
||||
|
||||
use chillerlan\QRCode\{QRCode, QROptions};
|
||||
use chillerlan\QRCode\Common\{EccLevel, MaskPattern, Mode, Version};
|
||||
use chillerlan\QRCodeTest\QRMaxLengthTrait;
|
||||
use Generator;
|
||||
use function file_put_contents;
|
||||
use function json_encode;
|
||||
use function printf;
|
||||
use function sprintf;
|
||||
use function str_repeat;
|
||||
use function substr;
|
||||
use const JSON_PRETTY_PRINT;
|
||||
|
||||
require_once __DIR__.'/../../vendor/autoload.php';
|
||||
|
||||
// excerpt from QRCodeReaderTestAbstract
|
||||
$generator = new class () {
|
||||
use QRMaxLengthTrait;
|
||||
|
||||
public function dataProvider():Generator{
|
||||
$str = str_repeat('https://www.youtube.com/watch?v=dQw4w9WgXcQ ', 100);
|
||||
$eccLevel = new EccLevel(EccLevel::H);
|
||||
|
||||
for($v = 1; $v <= 40; $v++){
|
||||
$version = new Version($v);
|
||||
|
||||
yield sprintf('version %2s%s', $version, $eccLevel) => [
|
||||
$version->getVersionNumber(),
|
||||
$eccLevel->getLevel(),
|
||||
substr($str, 0, self::getMaxLengthForMode(Mode::BYTE, $version, $eccLevel)),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$test = new PerformanceTest(100);
|
||||
$json = [];
|
||||
|
||||
foreach($generator->dataProvider() as $key => [$version, $eccLevel, $data]){
|
||||
$qrcode = new QRCode(new QROptions(['version' => $version, 'eccLevel' => $eccLevel]));
|
||||
$qrcode->addByteSegment($data);
|
||||
$matrix = $qrcode->getQRMatrix();
|
||||
|
||||
$test->run(fn() => MaskPattern::getBestPattern($matrix));
|
||||
|
||||
printf("%s: %01.3fms\n", $key, $test->getResult());
|
||||
$json[$version] = $test->getResult();
|
||||
}
|
||||
|
||||
file_put_contents(__DIR__.'/performance_maskpattern.json', json_encode($json, JSON_PRETTY_PRINT));
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* Tests the performance of the built-in output classes
|
||||
*
|
||||
* @created 16.10.2023
|
||||
* @author smiley <smiley@chillerlan.net>
|
||||
* @copyright 2023 smiley
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
namespace chillerlan\QRCodeTest\Performance;
|
||||
|
||||
use chillerlan\QRCode\{QRCode, QROptions};
|
||||
use chillerlan\QRCode\Common\{EccLevel, Mode, Version};
|
||||
use chillerlan\QRCode\Output\QROutputInterface;
|
||||
use chillerlan\QRCodeTest\QRMaxLengthTrait;
|
||||
use Generator;
|
||||
use function file_put_contents;
|
||||
use function json_encode;
|
||||
use function printf;
|
||||
use function sprintf;
|
||||
use function str_repeat;
|
||||
use function str_replace;
|
||||
use function substr;
|
||||
use const JSON_PRETTY_PRINT;
|
||||
|
||||
require_once __DIR__.'/../../vendor/autoload.php';
|
||||
|
||||
|
||||
// excerpt from QRCodeReaderTestAbstract
|
||||
$generator = new class () {
|
||||
use QRMaxLengthTrait;
|
||||
|
||||
public function dataProvider():Generator{
|
||||
$str = str_repeat('https://www.youtube.com/watch?v=dQw4w9WgXcQ ', 100);
|
||||
$eccLevel = new EccLevel(EccLevel::L);
|
||||
|
||||
for($v = 5; $v <= 40; $v += 5){
|
||||
$version = new Version($v);
|
||||
foreach(QROutputInterface::MODES as $outputType => $FQN){
|
||||
$name = str_replace('chillerlan\\QRCode\\Output\\', '', $FQN);
|
||||
|
||||
yield sprintf('version %2s: %-14s', $version, $name) => [
|
||||
$version->getVersionNumber(),
|
||||
$outputType,
|
||||
$FQN,
|
||||
substr($str, 0, self::getMaxLengthForMode(Mode::BYTE, $version, $eccLevel)),
|
||||
$name,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$test = new PerformanceTest(100);
|
||||
$json = [];
|
||||
|
||||
foreach($generator->dataProvider() as $key => [$version, $outputType, $FQN, $data, $name]){
|
||||
|
||||
$options = new QROptions([
|
||||
'version' => $version,
|
||||
'outputType' => $outputType,
|
||||
'connectPaths' => true,
|
||||
'drawLightModules' => true,
|
||||
'drawCircularModules' => true,
|
||||
'gdImageUseUpscale' => false, // set to false to allow proper comparison
|
||||
]);
|
||||
|
||||
$qrcode = new QRCode($options);
|
||||
$qrcode->addByteSegment($data);
|
||||
$matrix = $qrcode->getQRMatrix();
|
||||
|
||||
$test->run(fn() => (new ($FQN)($options, $matrix))->dump());
|
||||
|
||||
printf("%s: %8.3fms\n", $key, $test->getResult());
|
||||
$json[$name][$version] = $test->getResult();
|
||||
}
|
||||
|
||||
file_put_contents(__DIR__.'/performance_output.json', json_encode($json, JSON_PRETTY_PRINT));
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* Tests the overall performance of the QRCode class
|
||||
*
|
||||
* @created 19.10.2023
|
||||
* @author smiley <smiley@chillerlan.net>
|
||||
* @copyright 2023 smiley
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
namespace chillerlan\QRCodeTest\Performance;
|
||||
|
||||
use chillerlan\QRCode\{QRCode, QROptions};
|
||||
use chillerlan\QRCode\Common\{EccLevel, Mode, Version};
|
||||
use chillerlan\QRCode\Output\QROutputInterface;
|
||||
use chillerlan\QRCodeTest\QRMaxLengthTrait;
|
||||
use Generator;
|
||||
use function file_put_contents;
|
||||
use function json_encode;
|
||||
use function mb_substr;
|
||||
use function printf;
|
||||
use function sprintf;
|
||||
use function str_repeat;
|
||||
use function str_replace;
|
||||
use const JSON_PRETTY_PRINT;
|
||||
|
||||
require_once __DIR__.'/../../vendor/autoload.php';
|
||||
|
||||
// excerpt from QRCodeReaderTestAbstract
|
||||
$generator = new class () {
|
||||
use QRMaxLengthTrait;
|
||||
|
||||
public function dataProvider():Generator{
|
||||
|
||||
$dataModeData = [
|
||||
Mode::NUMBER => str_repeat('0123456789', 750),
|
||||
Mode::ALPHANUM => str_repeat('ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:', 100),
|
||||
Mode::KANJI => str_repeat('漂う花の香り', 350),
|
||||
Mode::HANZI => str_repeat('无可奈何燃花作香', 250),
|
||||
Mode::BYTE => str_repeat('https://www.youtube.com/watch?v=dQw4w9WgXcQ ', 100),
|
||||
];
|
||||
|
||||
foreach(Mode::INTERFACES as $dataMode => $dataModeInterface){
|
||||
$dataModeName = str_replace('chillerlan\\QRCode\\Data\\', '', $dataModeInterface);
|
||||
|
||||
for($v = 1; $v <= 40; $v++){
|
||||
$version = new Version($v);
|
||||
|
||||
foreach([EccLevel::L, EccLevel::M, EccLevel::Q, EccLevel::H] as $ecc){
|
||||
$eccLevel = new EccLevel($ecc);
|
||||
|
||||
yield sprintf('version %2s%s (%s)', $version, $eccLevel, $dataModeName) => [
|
||||
$version->getVersionNumber(),
|
||||
$eccLevel,
|
||||
$dataModeInterface,
|
||||
$dataModeName,
|
||||
mb_substr($dataModeData[$dataMode], 0, self::getMaxLengthForMode($dataMode, $version, $eccLevel)),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$test = new PerformanceTest(100);
|
||||
$json = [];
|
||||
|
||||
foreach($generator->dataProvider() as $key => [$version, $eccLevel, $dataModeInterface, $dataModeName, $data]){
|
||||
|
||||
$options = new QROptions([
|
||||
'outputType' => QROutputInterface::STRING_JSON,
|
||||
'version' => $version,
|
||||
'eccLevel' => $eccLevel->getLevel(),
|
||||
]);
|
||||
|
||||
$test->run(fn() => (new QRCode($options))->addSegment(new ($dataModeInterface)($data))->render());
|
||||
|
||||
printf("%s: %01.3fms\n", $key, $test->getResult());
|
||||
$json[$dataModeName][(string)$eccLevel][$version] = $test->getResult();
|
||||
}
|
||||
|
||||
file_put_contents(__DIR__.'/performance_qrcode.json', json_encode($json, JSON_PRETTY_PRINT));
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* Tests the QRMatrix write performance
|
||||
*
|
||||
* @created 16.10.2023
|
||||
* @author smiley <smiley@chillerlan.net>
|
||||
* @copyright 2023 smiley
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
namespace chillerlan\QRCodeTest\Performance;
|
||||
|
||||
use chillerlan\QRCode\Common\{EccLevel, Mode, Version};
|
||||
use chillerlan\QRCode\Data\QRData;
|
||||
use chillerlan\QRCode\QROptions;
|
||||
use chillerlan\QRCodeTest\QRMaxLengthTrait;
|
||||
use Generator;
|
||||
use function file_put_contents;
|
||||
use function json_encode;
|
||||
use function printf;
|
||||
use function sprintf;
|
||||
use function str_repeat;
|
||||
use function str_replace;
|
||||
use const JSON_PRETTY_PRINT;
|
||||
|
||||
require_once __DIR__.'/../../vendor/autoload.php';
|
||||
|
||||
// excerpt from QRCodeReaderTestAbstract
|
||||
$generator = new class () {
|
||||
use QRMaxLengthTrait;
|
||||
|
||||
public function dataProvider():Generator{
|
||||
|
||||
$dataModeData = [
|
||||
Mode::NUMBER => str_repeat('0123456789', 750),
|
||||
Mode::ALPHANUM => str_repeat('ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:', 100),
|
||||
Mode::KANJI => str_repeat('漂う花の香り', 350),
|
||||
Mode::HANZI => str_repeat('无可奈何燃花作香', 250),
|
||||
Mode::BYTE => str_repeat('https://www.youtube.com/watch?v=dQw4w9WgXcQ ', 100),
|
||||
];
|
||||
|
||||
foreach(Mode::INTERFACES as $dataMode => $dataModeInterface){
|
||||
$dataModeName = str_replace('chillerlan\\QRCode\\Data\\', '', $dataModeInterface);
|
||||
|
||||
for($v = 1; $v <= 40; $v++){
|
||||
$version = new Version($v);
|
||||
|
||||
foreach([EccLevel::L, EccLevel::M, EccLevel::Q, EccLevel::H] as $ecc){
|
||||
$eccLevel = new EccLevel($ecc);
|
||||
|
||||
yield sprintf('version %2s%s (%s)', $version, $eccLevel, $dataModeName) => [
|
||||
$version->getVersionNumber(),
|
||||
$eccLevel,
|
||||
$dataModeInterface,
|
||||
$dataModeName,
|
||||
mb_substr($dataModeData[$dataMode], 0, self::getMaxLengthForMode($dataMode, $version, $eccLevel)),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$test = new PerformanceTest(100);
|
||||
$json = [];
|
||||
|
||||
foreach($generator->dataProvider() as $key => [$version, $eccLevel, $dataModeInterface, $dataModeName, $data]){
|
||||
// invovcation tests the performance of QRData::writeBitBuffer()
|
||||
$test->run(fn() => new QRData(new QROptions(['version' => $version, 'eccLevel' => $eccLevel->getLevel()]), [new ($dataModeInterface)($data)]));
|
||||
|
||||
printf('%s encode: % 6.3fms', $key, $test->getResult());
|
||||
$json[$dataModeName][(string)$eccLevel]['encode'][$version] = $test->getResult();
|
||||
|
||||
// writeMatrix includes QRMatrix::writeCodewords() and the ReedSolomonEncoder
|
||||
$qrdata = new QRData(new QROptions(['version' => $version, 'eccLevel' => $eccLevel->getLevel()]), [new ($dataModeInterface)($data)]);
|
||||
$test->run(fn() => $qrdata->writeMatrix());
|
||||
|
||||
printf(', write matrix: % 6.3fms', $test->getResult());
|
||||
$json[$dataModeName][(string)$eccLevel]['write'][$version] = $test->getResult();
|
||||
|
||||
$bitBuffer = $qrdata->getBitBuffer();
|
||||
$bitBuffer->read(4); // read data mode indicator
|
||||
|
||||
$test->run(fn() => $dataModeInterface::decodeSegment(clone $bitBuffer, $version));
|
||||
|
||||
printf(", decode: % 6.3fms\n", $test->getResult());
|
||||
$json[$dataModeName][(string)$eccLevel]['decode'][$version] = $test->getResult();
|
||||
}
|
||||
|
||||
file_put_contents(__DIR__.'/performance_qrdata.json', json_encode($json, JSON_PRETTY_PRINT));
|
||||
Reference in New Issue
Block a user