Skip to content

Commit f839fd7

Browse files
perf(base58): add AVX2 (#479)
* perf(base58): add AVX2 * fix(base58): count leading zeros without TZCNT in AVX2 kernels
1 parent f8449ac commit f839fd7

10 files changed

Lines changed: 1134 additions & 8 deletions

File tree

base58/avx2_amd64.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//go:build amd64 && !purego
2+
3+
package base58
4+
5+
// Kernels in avx2_amd64.s. Encoders return the length n and clobber dst[n:].
6+
// Decoders take 32..44 / 64..88 chars and return 0 (ok), 1 (ErrInvalidChar)
7+
// or 2 (ErrValueTooLarge); leading-'1' validation is left to the caller.
8+
9+
//go:noescape
10+
func encode32AVX2(src *[32]byte, dst *[48]byte) int
11+
12+
//go:noescape
13+
func encode64AVX2(src *[64]byte, dst *[96]byte) int
14+
15+
//go:noescape
16+
func decode32AVX2(s *byte, n int, dst *[32]byte) int
17+
18+
//go:noescape
19+
func decode64AVX2(s *byte, n int, dst *[64]byte) int
20+
21+
// Tables widened to qwords (rows padded to 4-lane multiples) for VPMULUDQ.
22+
var (
23+
encTable32q [binarySz32][8]uint64
24+
encTable64q [binarySz64][20]uint64
25+
decTable32q [intermediateSz32][8]uint64
26+
decTable64q [intermediateSz64][16]uint64
27+
)
28+
29+
func init() {
30+
for i, row := range encTable32 {
31+
for k, v := range row {
32+
encTable32q[i][k] = uint64(v)
33+
}
34+
}
35+
for i, row := range encTable64 {
36+
for k, v := range row {
37+
encTable64q[i][k] = uint64(v)
38+
}
39+
}
40+
for i, row := range decTable32 {
41+
for k, v := range row {
42+
decTable32q[i][k] = uint64(v)
43+
}
44+
}
45+
for i, row := range decTable64 {
46+
for k, v := range row {
47+
decTable64q[i][k] = uint64(v)
48+
}
49+
}
50+
}
51+
52+
// rep tiles b into a 32-byte vector constant.
53+
//
54+
//nolint:unused // referenced from avx2_amd64.s
55+
func rep(b ...byte) (out [32]byte) {
56+
for i := range out {
57+
out[i] = b[i%len(b)]
58+
}
59+
return
60+
}
61+
62+
//nolint:unused // referenced from avx2_amd64.s
63+
var (
64+
avxBswap = rep(3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12)
65+
avxIota = rep(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
66+
avxSpread = rep(0, 1, 1, 1, 1, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
67+
avxP = rep(0, 1, 2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 0x80, 0x80, 0x80, 0x80)
68+
avxQ = rep(4, 0x80, 0x80, 0x80, 9, 0x80, 0x80, 0x80, 14, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80)
69+
avxTail = [32]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
70+
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0x80}
71+
avxW1 = rep(58, 1)
72+
avxW2 = rep(0x24, 0x0D, 1, 0) // int16 {3364, 1}
73+
// valid base58 char iff lo[c&15]&hi[c>>4] != 0
74+
avxLoNib = rep(20, 31, 31, 31, 31, 31, 31, 31, 31, 29, 30, 10, 2, 10, 10, 8)
75+
avxHiNib = rep(0, 0, 0, 1, 2, 4, 8, 16, 0, 0, 0, 0, 0, 0, 0, 0)
76+
avxB8 = rep(8)
77+
avxB16 = rep(16)
78+
avxB21 = rep(21)
79+
avxB32 = rep(32)
80+
avxB43 = rep(43)
81+
avxB64 = rep(64)
82+
avxB73 = rep(73)
83+
avxB79 = rep(79)
84+
avxB96 = rep(96)
85+
avxB108 = rep(108)
86+
avxBm6 = rep(0xFA) // -6
87+
avxBm7 = rep(0xF9) // -7
88+
avxBm49 = rep(0xCF) // -'1'
89+
avxB15 = rep(0x0F)
90+
avxD58 = rep(58, 0, 0, 0)
91+
avxDivA = rep(0x09, 0xCB, 0x3D, 0x8D, 0, 0, 0, 0) // 2369637129 = 2^37/58
92+
avxDivB = rep(0x93, 0x20, 0xED, 0x4D, 0, 0, 0, 0) // 1307386003 = 2^42/58^2
93+
)

0 commit comments

Comments
 (0)