Skip to content

Commit f6dc397

Browse files
committed
✨ cheap performance tests
1 parent 49b0c39 commit f6dc397

File tree

7 files changed

+369
-0
lines changed

7 files changed

+369
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ phpcs.xml
1818
phpdoc.xml
1919
phpmd.xml
2020
phpunit.xml
21+
tests/Performance/*.json

phpunit.xml.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<testsuites>
1010
<testsuite name="php-qrcode test suite">
1111
<directory suffix=".php">./tests/</directory>
12+
<exclude>./tests/Performance</exclude>
1213
</testsuite>
1314
</testsuites>
1415
<coverage processUncoveredFiles="true">

tests/Performance/PerformanceTest.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
/**
3+
* Class PerformanceTest
4+
*
5+
* @created 16.10.2023
6+
* @author smiley <[email protected]>
7+
* @copyright 2023 smiley
8+
* @license MIT
9+
*/
10+
11+
namespace chillerlan\QRCodeTest\Performance;
12+
13+
use Closure;
14+
use function hrtime;
15+
16+
/**
17+
*
18+
*/
19+
class PerformanceTest{
20+
21+
protected int $runs;
22+
protected int $total = 0;
23+
24+
public function __construct(int $runs = 1000){
25+
$this->runs = $runs;
26+
}
27+
28+
public function run(Closure $subject):self{
29+
$this->total = 0;
30+
31+
for($i = 0; $i < $this->runs; $i++){
32+
$start = hrtime(true);
33+
34+
$subject();
35+
36+
$end = hrtime(true);
37+
38+
$this->total += ($end - $start);
39+
}
40+
41+
return $this;
42+
}
43+
44+
public function getResult():float{
45+
return ($this->total / $this->runs / 1000000);
46+
}
47+
48+
}

tests/Performance/maskpattern.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
/**
3+
* Tests the performance of the mask pattern penalty testing
4+
*
5+
* @created 18.10.2023
6+
* @author smiley <[email protected]>
7+
* @copyright 2023 smiley
8+
* @license MIT
9+
*/
10+
11+
namespace chillerlan\QRCodeTest\Performance;
12+
13+
use chillerlan\QRCode\{QRCode, QROptions};
14+
use chillerlan\QRCode\Common\{EccLevel, MaskPattern, Mode, Version};
15+
use chillerlan\QRCodeTest\QRMaxLengthTrait;
16+
use Generator;
17+
use function file_put_contents;
18+
use function json_encode;
19+
use function printf;
20+
use function sprintf;
21+
use function str_repeat;
22+
use function substr;
23+
use const JSON_PRETTY_PRINT;
24+
25+
require_once __DIR__.'/../../vendor/autoload.php';
26+
27+
// excerpt from QRCodeReaderTestAbstract
28+
$generator = new class () {
29+
use QRMaxLengthTrait;
30+
31+
public function dataProvider():Generator{
32+
$str = str_repeat('https://www.youtube.com/watch?v=dQw4w9WgXcQ ', 100);
33+
$eccLevel = new EccLevel(EccLevel::H);
34+
35+
for($v = 1; $v <= 40; $v++){
36+
$version = new Version($v);
37+
38+
yield sprintf('version %2s%s', $version, $eccLevel) => [
39+
$version->getVersionNumber(),
40+
$eccLevel->getLevel(),
41+
substr($str, 0, self::getMaxLengthForMode(Mode::BYTE, $version, $eccLevel)),
42+
];
43+
}
44+
}
45+
46+
};
47+
48+
$test = new PerformanceTest(100);
49+
$json = [];
50+
51+
foreach($generator->dataProvider() as $key => [$version, $eccLevel, $data]){
52+
$qrcode = new QRCode(new QROptions(['version' => $version, 'eccLevel' => $eccLevel]));
53+
$qrcode->addByteSegment($data);
54+
$matrix = $qrcode->getQRMatrix();
55+
56+
$test->run(fn() => MaskPattern::getBestPattern($matrix));
57+
58+
printf("%s: %01.3fms\n", $key, $test->getResult());
59+
$json[$version] = $test->getResult();
60+
}
61+
62+
file_put_contents(__DIR__.'/performance_maskpattern.json', json_encode($json, JSON_PRETTY_PRINT));

tests/Performance/output.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
/**
3+
* Tests the performance of the built-in output classes
4+
*
5+
* @created 16.10.2023
6+
* @author smiley <[email protected]>
7+
* @copyright 2023 smiley
8+
* @license MIT
9+
*/
10+
11+
namespace chillerlan\QRCodeTest\Performance;
12+
13+
use chillerlan\QRCode\{QRCode, QROptions};
14+
use chillerlan\QRCode\Common\{EccLevel, Mode, Version};
15+
use chillerlan\QRCode\Output\QROutputInterface;
16+
use chillerlan\QRCodeTest\QRMaxLengthTrait;
17+
use Generator;
18+
use function file_put_contents;
19+
use function json_encode;
20+
use function printf;
21+
use function sprintf;
22+
use function str_repeat;
23+
use function str_replace;
24+
use function substr;
25+
use const JSON_PRETTY_PRINT;
26+
27+
require_once __DIR__.'/../../vendor/autoload.php';
28+
29+
30+
// excerpt from QRCodeReaderTestAbstract
31+
$generator = new class () {
32+
use QRMaxLengthTrait;
33+
34+
public function dataProvider():Generator{
35+
$str = str_repeat('https://www.youtube.com/watch?v=dQw4w9WgXcQ ', 100);
36+
$eccLevel = new EccLevel(EccLevel::L);
37+
38+
for($v = 5; $v <= 40; $v += 5){
39+
$version = new Version($v);
40+
foreach(QROutputInterface::MODES as $outputType => $FQN){
41+
$name = str_replace('chillerlan\\QRCode\\Output\\', '', $FQN);
42+
43+
yield sprintf('version %2s: %-14s', $version, $name) => [
44+
$version->getVersionNumber(),
45+
$outputType,
46+
$FQN,
47+
substr($str, 0, self::getMaxLengthForMode(Mode::BYTE, $version, $eccLevel)),
48+
$name,
49+
];
50+
}
51+
}
52+
53+
}
54+
55+
};
56+
57+
$test = new PerformanceTest(100);
58+
$json = [];
59+
60+
foreach($generator->dataProvider() as $key => [$version, $outputType, $FQN, $data, $name]){
61+
62+
$options = new QROptions([
63+
'version' => $version,
64+
'outputType' => $outputType,
65+
'connectPaths' => true,
66+
'drawLightModules' => true,
67+
'drawCircularModules' => true,
68+
'gdImageUseUpscale' => false, // set to false to allow proper comparison
69+
]);
70+
71+
$qrcode = new QRCode($options);
72+
$qrcode->addByteSegment($data);
73+
$matrix = $qrcode->getQRMatrix();
74+
75+
$test->run(fn() => (new ($FQN)($options, $matrix))->dump());
76+
77+
printf("%s: %8.3fms\n", $key, $test->getResult());
78+
$json[$name][$version] = $test->getResult();
79+
}
80+
81+
file_put_contents(__DIR__.'/performance_output.json', json_encode($json, JSON_PRETTY_PRINT));

tests/Performance/qrcode.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
/**
3+
* Tests the overall performance of the QRCode class
4+
*
5+
* @created 19.10.2023
6+
* @author smiley <[email protected]>
7+
* @copyright 2023 smiley
8+
* @license MIT
9+
*/
10+
11+
namespace chillerlan\QRCodeTest\Performance;
12+
13+
use chillerlan\QRCode\{QRCode, QROptions};
14+
use chillerlan\QRCode\Common\{EccLevel, Mode, Version};
15+
use chillerlan\QRCode\Output\QROutputInterface;
16+
use chillerlan\QRCodeTest\QRMaxLengthTrait;
17+
use Generator;
18+
use function file_put_contents;
19+
use function json_encode;
20+
use function mb_substr;
21+
use function printf;
22+
use function sprintf;
23+
use function str_repeat;
24+
use function str_replace;
25+
use const JSON_PRETTY_PRINT;
26+
27+
require_once __DIR__.'/../../vendor/autoload.php';
28+
29+
// excerpt from QRCodeReaderTestAbstract
30+
$generator = new class () {
31+
use QRMaxLengthTrait;
32+
33+
public function dataProvider():Generator{
34+
35+
$dataModeData = [
36+
Mode::NUMBER => str_repeat('0123456789', 750),
37+
Mode::ALPHANUM => str_repeat('ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:', 100),
38+
Mode::KANJI => str_repeat('漂う花の香り', 350),
39+
Mode::HANZI => str_repeat('无可奈何燃花作香', 250),
40+
Mode::BYTE => str_repeat('https://www.youtube.com/watch?v=dQw4w9WgXcQ ', 100),
41+
];
42+
43+
foreach(Mode::INTERFACES as $dataMode => $dataModeInterface){
44+
$dataModeName = str_replace('chillerlan\\QRCode\\Data\\', '', $dataModeInterface);
45+
46+
for($v = 1; $v <= 40; $v++){
47+
$version = new Version($v);
48+
49+
foreach([EccLevel::L, EccLevel::M, EccLevel::Q, EccLevel::H] as $ecc){
50+
$eccLevel = new EccLevel($ecc);
51+
52+
yield sprintf('version %2s%s (%s)', $version, $eccLevel, $dataModeName) => [
53+
$version->getVersionNumber(),
54+
$eccLevel,
55+
$dataModeInterface,
56+
$dataModeName,
57+
mb_substr($dataModeData[$dataMode], 0, self::getMaxLengthForMode($dataMode, $version, $eccLevel)),
58+
];
59+
}
60+
}
61+
}
62+
63+
}
64+
65+
};
66+
67+
$test = new PerformanceTest(100);
68+
$json = [];
69+
70+
foreach($generator->dataProvider() as $key => [$version, $eccLevel, $dataModeInterface, $dataModeName, $data]){
71+
72+
$options = new QROptions([
73+
'outputType' => QROutputInterface::STRING_JSON,
74+
'version' => $version,
75+
'eccLevel' => $eccLevel->getLevel(),
76+
]);
77+
78+
$test->run(fn() => (new QRCode($options))->addSegment(new ($dataModeInterface)($data))->render());
79+
80+
printf("%s: %01.3fms\n", $key, $test->getResult());
81+
$json[$dataModeName][(string)$eccLevel][$version] = $test->getResult();
82+
}
83+
84+
file_put_contents(__DIR__.'/performance_qrcode.json', json_encode($json, JSON_PRETTY_PRINT));

tests/Performance/qrdata.php

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
/**
3+
* Tests the QRMatrix write performance
4+
*
5+
* @created 16.10.2023
6+
* @author smiley <[email protected]>
7+
* @copyright 2023 smiley
8+
* @license MIT
9+
*/
10+
11+
namespace chillerlan\QRCodeTest\Performance;
12+
13+
use chillerlan\QRCode\Common\{EccLevel, Mode, Version};
14+
use chillerlan\QRCode\Data\QRData;
15+
use chillerlan\QRCode\QROptions;
16+
use chillerlan\QRCodeTest\QRMaxLengthTrait;
17+
use Generator;
18+
use function file_put_contents;
19+
use function json_encode;
20+
use function printf;
21+
use function sprintf;
22+
use function str_repeat;
23+
use function str_replace;
24+
use const JSON_PRETTY_PRINT;
25+
26+
require_once __DIR__.'/../../vendor/autoload.php';
27+
28+
// excerpt from QRCodeReaderTestAbstract
29+
$generator = new class () {
30+
use QRMaxLengthTrait;
31+
32+
public function dataProvider():Generator{
33+
34+
$dataModeData = [
35+
Mode::NUMBER => str_repeat('0123456789', 750),
36+
Mode::ALPHANUM => str_repeat('ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:', 100),
37+
Mode::KANJI => str_repeat('漂う花の香り', 350),
38+
Mode::HANZI => str_repeat('无可奈何燃花作香', 250),
39+
Mode::BYTE => str_repeat('https://www.youtube.com/watch?v=dQw4w9WgXcQ ', 100),
40+
];
41+
42+
foreach(Mode::INTERFACES as $dataMode => $dataModeInterface){
43+
$dataModeName = str_replace('chillerlan\\QRCode\\Data\\', '', $dataModeInterface);
44+
45+
for($v = 1; $v <= 40; $v++){
46+
$version = new Version($v);
47+
48+
foreach([EccLevel::L, EccLevel::M, EccLevel::Q, EccLevel::H] as $ecc){
49+
$eccLevel = new EccLevel($ecc);
50+
51+
yield sprintf('version %2s%s (%s)', $version, $eccLevel, $dataModeName) => [
52+
$version->getVersionNumber(),
53+
$eccLevel,
54+
$dataModeInterface,
55+
$dataModeName,
56+
mb_substr($dataModeData[$dataMode], 0, self::getMaxLengthForMode($dataMode, $version, $eccLevel)),
57+
];
58+
}
59+
}
60+
}
61+
62+
}
63+
64+
};
65+
66+
$test = new PerformanceTest(100);
67+
$json = [];
68+
69+
foreach($generator->dataProvider() as $key => [$version, $eccLevel, $dataModeInterface, $dataModeName, $data]){
70+
// invovcation tests the performance of QRData::writeBitBuffer()
71+
$test->run(fn() => new QRData(new QROptions(['version' => $version, 'eccLevel' => $eccLevel->getLevel()]), [new ($dataModeInterface)($data)]));
72+
73+
printf('%s encode: % 6.3fms', $key, $test->getResult());
74+
$json[$dataModeName][(string)$eccLevel]['encode'][$version] = $test->getResult();
75+
76+
// writeMatrix includes QRMatrix::writeCodewords() and the ReedSolomonEncoder
77+
$qrdata = new QRData(new QROptions(['version' => $version, 'eccLevel' => $eccLevel->getLevel()]), [new ($dataModeInterface)($data)]);
78+
$test->run(fn() => $qrdata->writeMatrix());
79+
80+
printf(', write matrix: % 6.3fms', $test->getResult());
81+
$json[$dataModeName][(string)$eccLevel]['write'][$version] = $test->getResult();
82+
83+
$bitBuffer = $qrdata->getBitBuffer();
84+
$bitBuffer->read(4); // read data mode indicator
85+
86+
$test->run(fn() => $dataModeInterface::decodeSegment(clone $bitBuffer, $version));
87+
88+
printf(", decode: % 6.3fms\n", $test->getResult());
89+
$json[$dataModeName][(string)$eccLevel]['decode'][$version] = $test->getResult();
90+
}
91+
92+
file_put_contents(__DIR__.'/performance_qrdata.json', json_encode($json, JSON_PRETTY_PRINT));

0 commit comments

Comments
 (0)