Closed odan closed 2 years ago
5 seconds sounds like an awful lot of time for generating a single QR-Code. Can you provide a test-code (BaconQrCode native) to reproduce that time? All my tests generate QR-Codes in a fraction of that time.
Thanks for the feedback. I will try to prepare a test-code and paste it here.
Ok, here is a minimal working example to reproduce the performance issue:
use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Encoder\Encoder;
// ...
$text = str_repeat('A', 270);
$baconErrorCorrectionLevel = ErrorCorrectionLevel::valueOf('L');
$encoding = 'UTF-8';
$baconQrCode = Encoder::encode($text, $baconErrorCorrectionLevel, $encoding);
The Encoder::encode
method takes ~10 seconds for 270 characters with Xdebug enabled. We use this also to generate the code coverage.
Time: 10.16 seconds, Memory: 10.00MB
With Xdebug disabled, it takes ~500 ms.
Time: 519 ms, Memory: 10.00MB
This is still relative slow.
Now let's take a look at the profiler and sort by number of calls.
If I counted correctly, there are over ~129.000 ! internal method calls to process this one QR code, which could be the cause of this performance issue.
Within the Encoder::encode
method, this line with self::chooseMaskPattern(...)
requires nearly all the time for the calculation, the other parts are fast.
$maskPattern = self::chooseMaskPattern($finalBits, $ecLevel, $version, $matrix);
https://github.com/Bacon/BaconQrCode/blob/2.0.6/src/Encoder/Encoder.php#L135
Thanks, that's a pretty in-depth analysis! One thing which stands out to me is that over half the time is spent on MaskUtil::getDataMaskBit()
. This is particularly interesting because it's just a very simple switch statement with some bit masking.
This can't come from the overhead of function calls in PHP itself, as other functions are called more often and take way less time. Do you have any clue what might make this specific function so expensive?
Sorry, after trying the whole day, I have no clue what's going on there. I tried to find a concrete bottleneck, but I found at least two methods calls.
MatrixUtil::embedDataBits
$maskPattern = self::chooseMaskPattern($finalBits, $ecLevel, $version, $matrix);
The time sum of these methods is by far the largest.
So my conclusion (guess) is that it's the sheer mass of bit operations inside plenty of nested foreach and for loops. I have tried to optimize some functions but without a real performance gain. So I close this issue for future reference.
Yeah, PHP is not really suited for this kind of stuff. Thanks though for investing time into this!
One option to speed things up would be to utilize qrencode
binary if installed on the system, in which case it could use that to generate an ASCII QR-Code, parse that into a matrix array and then continue as usual.
This would only be helpful though if you have access to the server running your PHP code to install that package.
Hi!
I'm using the sprain/php-swiss-qr-bill library to generate QR-ESRs. Generating a single QR-ESR takes ~5 seconds (or more).
Then I started the profiler and debugged it manually until I found that only the
BaconQrCode\Encoder::chooseMaskPattern
needed nearly all of this time. The time for PDF rendering etc. is nothing compared to this single method.https://github.com/Bacon/BaconQrCode/blob/master/src/Encoder/Encoder.php#L233-L252
My question, is there a possibility to pass "some arguments" to make it faster to generate the QR code?