Skip to content

Performance improvements#509

Merged
codebude merged 32 commits intomasterfrom
performance
May 3, 2024
Merged

Performance improvements#509
codebude merged 32 commits intomasterfrom
performance

Conversation

@Shane32
Copy link
Owner

@Shane32 Shane32 commented Apr 30, 2024

System

BenchmarkDotNet v0.13.12, Windows 10 (10.0.19045.4291/22H2/2022Update)
13th Gen Intel Core i9-13900K, 1 CPU, 32 logical and 24 physical cores
.NET SDK 8.0.202
  [Host]     : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2

Before

Method Mean Error StdDev Gen0 Gen1 Allocated
CreateQRCode 245.4 us 4.88 us 5.80 us 17.0898 0.2441 317.99 KB
CreateQRCodeLong 7,531.1 us 34.13 us 31.92 us 265.6250 15.6250 4920.13 KB

After

Method Mean Error StdDev Gen0 Allocated
CreateQRCode 100.7 us 0.26 us 0.22 us 1.2207 23.18 KB
CreateQRCodeLong 1,842.1 us 3.19 us 2.83 us 23.4375 433.53 KB

Notes

Most of the performance improvements in this PR fall into a few categories:

  • Use BitArray instead of string
  • Eliminate concatenation and instead write/copy into buffers using offsets and counts
  • Preallocation of arrays/lists to proper length
  • Eliminate array allocation when unnecessary (e.g. don't call AddRange)
  • Eliminate LINQ use
  • Improve lookup tables / dictionaries

Progress

So far about half the code has been converted to use BitArray. There is still a notable amount of code that still uses strings that remains to be optimized. Also the Polynom class/constructor is heavily used and should be optimized.

@Shane32
Copy link
Owner Author

Shane32 commented Apr 30, 2024

FYI, I have not yet gone back through all my changes to add comments and perform a self-review, etc.

@Shane32
Copy link
Owner Author

Shane32 commented Apr 30, 2024

By the way, I plan to attempt using ArrayPool for further optimizations but not in this PR.

@Shane32
Copy link
Owner Author

Shane32 commented May 1, 2024

@codebude I think this PR has enough changes. The GitHub comparison should be reasonable enough to follow. I would like to follow up with some other PRs:

  • Improve readability (code comments, rename variables, split into multiple files, etc)
  • Fix bug (?) with string handling
  • Utilize ArrayPool and other memory optimizations

@Shane32 Shane32 changed the title [WIP] Performance improvements Performance improvements May 1, 2024
@Shane32
Copy link
Owner Author

Shane32 commented May 1, 2024

The benchmarks are updated in the PR description, showing 60-75% memory reduction and 100-400% speed increase. Notably, Gen1 collections have been reduced enough that BenchmarkDotNet has stopped listing them!

Most likely further PRs will further reduce memory requirements, but not increase speed.

@Shane32
Copy link
Owner Author

Shane32 commented May 1, 2024

I'm not planning to re-engineer the mathematics, like to use vector math or anything. While I'm sure it's possible, that's not my forte.

Shane32 and others added 5 commits May 2, 2024 08:55
Co-authored-by: Günther Foidl <gue@korporal.at>
Co-authored-by: Günther Foidl <gue@korporal.at>
Co-authored-by: Günther Foidl <gue@korporal.at>
Co-authored-by: Günther Foidl <gue@korporal.at>
Co-authored-by: Günther Foidl <gue@korporal.at>
@Shane32
Copy link
Owner Author

Shane32 commented May 3, 2024

Benchmarks updated. New test results shaved 10us (10%) from short test and 20us (1%) from long test.

@Shane32
Copy link
Owner Author

Shane32 commented May 3, 2024

Eliminating foreach across a BitArray and implementing IEquatable for Point cut down memory requirements for small QR codes by an additional 80%, and an additional 60% for large QR codes.

@codebude
Copy link
Collaborator

codebude commented May 3, 2024

Benchmarks updated. New test results shaved 10us (10%) from short test and 20us (1%) from long test.

But memory usage improved massively. :-)

@Shane32
Copy link
Owner Author

Shane32 commented May 3, 2024

While all beneficial changes, the memory usage didn't change due to the arithmetic/ref/pointer changes; it measured as identical. The memory usage changes were due to foreach across a BitArray internally cast a bool to an object causing a heap allocation for each bit. Ditto for List<Point>.Contains due to Point not implementing IEquatable. These were accessed across some 'hot' code, and so many unnecessary heap allocations were made.

Copy link
Collaborator

@gfoidl gfoidl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few nits, otherwise LGTM 👍🏻

@Shane32 Shane32 requested review from codebude and gfoidl May 3, 2024 14:15
@codebude codebude self-requested a review May 3, 2024 14:37
@codebude codebude merged commit e36d34d into Shane32:master May 3, 2024
@codebude
Copy link
Collaborator

codebude commented May 3, 2024

Merged. Thanks for your great work @Shane32 & @gfoidl ! 👏

@Shane32 Shane32 deleted the performance branch May 3, 2024 18:51
@Shane32 Shane32 added the performance Performance related enhancements or benchmarks label Oct 2, 2025
@Shane32 Shane32 added this to the 1.6.0 milestone Oct 8, 2025
vaernion pushed a commit to Arbeidstilsynet/brevgen2 that referenced this pull request Dec 3, 2025
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [QRCoder](https://github.com/codebude/QRCoder) | `1.4.3` -> `1.6.0` | [![age](https://developer.mend.io/api/mc/badges/age/nuget/QRCoder/1.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/nuget/QRCoder/1.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/nuget/QRCoder/1.4.3/1.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/QRCoder/1.4.3/1.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>codebude/QRCoder (QRCoder)</summary>

### [`v1.6.0`](https://github.com/codebude/QRCoder/releases/tag/v1.6.0)

#### What has changed

-   \[Enhancement] Huge performance (runtime and memory) optimization in `QrCodeGenerator` (PRs [#&#8203;509](Shane32/QRCoder#509), [#&#8203;519](Shane32/QRCoder#519), [#&#8203;520](Shane32/QRCoder#520), [#&#8203;521](Shane32/QRCoder#521), [#&#8203;524](Shane32/QRCoder#524), [#&#8203;530](Shane32/QRCoder#530), [#&#8203;532](Shane32/QRCoder#532))
-   \[Enhancement] Big performance (runtime and memory) optimization in `BitmapByteQRCode` (PR [#&#8203;566](Shane32/QRCoder#566))
-   \[Enhancement] Added ECC level mismatch detection when using payloads from payload generator (PR [#&#8203;526](Shane32/QRCoder#526))
-   \[Enhancement] Implementation of encoding constraints (EccLevel, EciMode) in SwissQRCode and GiroCode payloads to enforce that the payloads comply with the specifications of the respective standards (PR [#&#8203;533](Shane32/QRCoder#533))
-   \[Enhancement] Added support for trimming (`IsTrimmable` project property) (PR [#&#8203;539](Shane32/QRCoder#539))
-   \[Enhancement] Added XML comments (code documentation) to all public members (PR [#&#8203;561](Shane32/QRCoder#561))
-   \[Enhancement] Added new/third overload to `PngByteQRCode` that accepts colors as System.Drawing.Color (PR [#&#8203;564](Shane32/QRCoder#564))
-   \[Enhancement] Updated the Bitmap header from BITMAPCOREHEADER to BITMAPINFOHEADER in `BitmapByteQRCode` (PR [#&#8203;565](Shane32/QRCoder#565))
-   \[Refactoring] Restructured `QRCodeGenerator` classes into separate files and partially restructured code for better manageability (PRs [#&#8203;516](Shane32/QRCoder#516), [#&#8203;528](Shane32/QRCoder#528), [#&#8203;531](https://github....
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance Performance related enhancements or benchmarks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants