Skip to content

Commit

Permalink
Merge pull request #105 from yeqown/optimise/load-alignment-pattern-loc
Browse files Browse the repository at this point in the history
feat: using precalculation for alignPattern locations
  • Loading branch information
yeqown authored May 3, 2024
2 parents eab5cc3 + 66ed9bd commit b8b0a29
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 8 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@ default.jpeg
.DS_store
tmp.png

go.sum
go.sum

# Go Test
new.txt
old.txt
2 changes: 1 addition & 1 deletion qrcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ func (q *QRCode) prefillMatrix() {
// only version-1 QR code has no alignment module
if q.v.Ver > 1 {
// add align-mode related to version cfg
for _, loc := range loadAlignmentPatternLoc(q.v.Ver) {
for _, loc := range loadAlignmentPatternLocV2(q.v.Ver) {
addAlignment(q.mat, loc.X, loc.Y)
}
debugLogf("finish align")
Expand Down
64 changes: 62 additions & 2 deletions version.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import (
"github.com/yeqown/reedsolomon/binary"
)

func init() {
precalculateAlignPatternLocs()
}

// ecLevel error correction level
type ecLevel int

Expand Down Expand Up @@ -354,8 +358,10 @@ var (
40: {6, 30, 58, 86, 114, 142, 170},
}

alignPatternCache = map[int][]loc{}
alignPatternCache = map[int][]loc{}
// TODO(@yeqown): remove this lock later, if alignPatternCache precalculation works well.
alignPatternCacheMu sync.Mutex
precalculateOnce sync.Once
)

// loc point position(x,y)
Expand All @@ -364,7 +370,8 @@ type loc struct {
Y int // for height
}

// loadAlignmentPatternLoc ...
// loadAlignmentPatternLoc load alignment pattern location by version
// @Deprecated
func loadAlignmentPatternLoc(ver int) (locs []loc) {
if ver < 2 {
return
Expand Down Expand Up @@ -396,6 +403,59 @@ func loadAlignmentPatternLoc(ver int) (locs []loc) {
return
}

func loadAlignmentPatternLocV2(ver int) []loc {
if ver < 2 {
return nil
}

if locs, ok := alignPatternCache[ver]; ok {
return locs
}

// Just in case, we need to calculate the alignment pattern locations
locs := calcAlignPatternLocs(ver)
alignPatternCacheMu.Lock()
alignPatternCache[ver] = locs
alignPatternCacheMu.Unlock()

return locs
}

// precalculateAlignPatternLocs precalculate all versions' alignment pattern locations which
// only need to be calculated once.
func precalculateAlignPatternLocs() {
precalculateOnce.Do(func() {
for ver := 2; ver <= _VERSION_COUNT; ver++ {
alignPatternCache[ver] = calcAlignPatternLocs(ver)
}
})
}

func calcAlignPatternLocs(ver int) (locs []loc) {
if ver < 2 {
return
}

dimension := ver*4 + 17
positions, ok := alignPatternLocation[ver]
if !ok {
panic("could not found align at version: " + strconv.Itoa(ver))
}

locs = make([]loc, 0, len(positions)*len(positions))

for _, pos1 := range positions {
for _, pos2 := range positions {
if !valid(pos1, pos2, dimension) {
continue
}
locs = append(locs, loc{X: pos1, Y: pos2})
}
}

return locs
}

// x, y center position x,y so
func valid(x, y, dimension int) bool {
// valid left-top
Expand Down
19 changes: 15 additions & 4 deletions version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"testing"

"github.com/stretchr/testify/require"

"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -228,7 +227,7 @@ func Test_binarySearchVersion_all(t *testing.T) {
assert.True(t, found)
assert.Equal(t, v, hit)

//t.Logf("finding: version=%d, ecLevel=%d", v.Ver, v.ECLevel)
// t.Logf("finding: version=%d, ecLevel=%d", v.Ver, v.ECLevel)
}
}

Expand All @@ -239,7 +238,7 @@ func Test_loadAlignmentPatternLoc_concurrentAccess(t *testing.T) {
wg.Add(1)

go func(v int) {
got := loadAlignmentPatternLoc(v)
got := loadAlignmentPatternLocV2(v)
assert.NotEmpty(t, got)
wg.Done()
}(ver)
Expand All @@ -248,7 +247,19 @@ func Test_loadAlignmentPatternLoc_concurrentAccess(t *testing.T) {
wg.Wait()
}

// // go test -run=NONE -bench . -count 10 > new/old.txt
func Benchmark_LoadAlignmentPatternLoc(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = loadAlignmentPatternLoc(5)
}
}

func Benchmark_LoadAlignmentPatternLocV2(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = loadAlignmentPatternLocV2(5)
}
}

// go test -run=NONE -bench . -count 10 > new/old.txt
func Benchmark_loadVersion_top(b *testing.B) {
for i := 0; i < b.N; i++ {
loadVersion(2, ErrorCorrectionMedium)
Expand Down

0 comments on commit b8b0a29

Please sign in to comment.