You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The benchmark history behind the profiling project: one entry per optimization step,
recorded when the step was made.
Entries are in chronological order, oldest first. Each one shows the state of the library after that
change and is left as it was written; the log is not updated retroactively.
Measurements are comparable only within a sample data era. The sample data changed once, at
Sample Data Change. Numbers on either side of that point mean different
things and must not be compared.
Cache the generator polynomial multiplied by every element of the field instead of the polynomial
alone. The division is then a shift and an exclusive or per data codeword, eight coefficients at a
time, with no field arithmetic left in the loop. The codewords are written straight into the
interleaved result, at a stride, so no block needs a buffer of its own.
The 0.02 MB that remain are the packed ulong[] holding the remainder, which is a little larger
than the byte[] it replaces.
Row Layouts
Two changes to the penalty rules, which is where encoding spends most of its time.
The finder-pattern rule no longer slides a 15-bit window one column at a time. A whole word is
matched at once: shifting the row lines up the module at each fixed offset from a candidate start,
so one sequence of shifts, ands and a population count finds every match beginning in that word.
BitMatrix then gained three row layouts. A row holds its modules in one, two or three 64-bit
words instead of always four, so a rule scans one word per row for versions 1 to 11, two for
versions 12 to 27 and three for versions 28 to 40. Every row-scanning rule has an implementation
per layout with its loop over the words unrolled. The stride between rows stays a power of two —
1, 2 or 4 — so a row index is still a shift, and the three-word layout keeps a fourth, always-zero
padding word that lets whole-matrix operations run flat over the raw array.
Versions 1 to 11 are most QR codes, and the sample data is no exception. Of the 4.1 s saved on the
profile loop, the word-parallel finder rule accounts for 1.7 s and the row layouts for 2.5 s.
The matrices of the smaller versions are a quarter of their former size, which is where the drop
in allocation comes from.
Sample Data Change
The sample data changed at this point, from 200 short payloads to 400 payloads that include
long texts. Every measurement below was taken on the new data, every measurement above on the old.
The entry that follows is therefore slower than the one before it even though nothing got slower:
it encodes twice as many payloads, and the added ones are much larger.
Larger Sample Data
The sample data covered versions 1 to 13 only, so the two- and three-word row layouts of
BitMatrix were barely measured. It now holds 400 payloads instead of 200, and one in five of
them is a long text of 400 to 900 characters assembled from the same fragments (sentences, names,
towns, URLs, numbers, messages). Long payloads reach versions 12 to 36, the short ones stay in
versions 1 to 11.
The measurements below are the new baseline; they are not comparable to the sections above, which
all used the 200 short payloads.
Twice the payloads, and the long ones cost far more than a short one: the penalty rules scan a
matrix that grows with the square of the version, and eight mask patterns are scored per code.
The segment compaction now assigns the segment modes by a dynamic programme over the blocks
instead of two greedy merge passes, and the compaction runs per version group (1–9, 10–26, 27–40)
instead of once for the maximum version. The result is the shortest possible bit stream for the
chosen version; the compaction mode reports no case where QRCoder's segments are shorter.
The checksum differs from the sections above because some QR codes got smaller.