Skip to content

Commit e2485a6

Browse files
committed
Improved benchmarking microtest
1 parent c3c3a72 commit e2485a6

File tree

2 files changed

+119
-88
lines changed

2 files changed

+119
-88
lines changed

benchmark_test.go

Lines changed: 85 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -4,72 +4,71 @@
44
// license that can be found in the LICENSE file.
55
//
66

7-
package semver_test
7+
package semver
88

99
import (
1010
"testing"
11-
12-
semver "go.bug.st/relaxed-semver"
1311
)
1412

15-
func BenchmarkVersionParser(b *testing.B) {
16-
list := []string{
17-
"0.0.1-rc.0", // 0
18-
"0.0.1-rc.0+build", // 1
19-
"0.0.1-rc.1", // 2
20-
"0.0.1", // 3
21-
"0.0.1+build", // 4
22-
"0.0.2-rc.1", // 5 - BREAKING CHANGE
23-
"0.0.2-rc.1+build", // 6
24-
"0.0.2", // 7
25-
"0.0.2+build", // 8
26-
"0.0.3-rc.1", // 9 - BREAKING CHANGE
27-
"0.0.3-rc.2", // 10
28-
"0.0.3", // 11
29-
"0.1.0", // 12 - BREAKING CHANGE
30-
"0.3.3-rc.0", // 13 - BREAKING CHANGE
31-
"0.3.3-rc.1", // 14
32-
"0.3.3", // 15
33-
"0.3.3+build", // 16
34-
"0.3.4-rc.1", // 17
35-
"0.3.4", // 18
36-
"0.4.0", // 19 - BREAKING CHANGE
37-
"1.0.0-rc", // 20 - BREAKING CHANGE
38-
"1.0.0", // 21
39-
"1.0.0+build", // 22
40-
"1.2.1-rc", // 23
41-
"1.2.1", // 24
42-
"1.2.1+build", // 25
43-
"1.2.3-rc.2", // 26
44-
"1.2.3-rc.2+build", // 27
45-
"1.2.3", // 28
46-
"1.2.3+build", // 29
47-
"1.2.4", // 30
48-
"1.3.0-rc.0+build", // 31
49-
"1.3.0", // 32
50-
"1.3.0+build", // 33
51-
"1.3.1-rc.0", // 34
52-
"1.3.1-rc.1", // 35
53-
"1.3.1", // 36
54-
"1.3.5", // 37
55-
"2.0.0-rc", // 38 - BREAKING CHANGE
56-
"2.0.0-rc+build", // 39
57-
"2.0.0", // 40
58-
"2.0.0+build", // 41
59-
"2.1.0-rc", // 42
60-
"2.1.0-rc+build", // 43
61-
"2.1.0", // 44
62-
"2.1.0+build", // 45
63-
"2.1.3-rc", // 46
64-
"2.1.3", // 47
65-
"2.3.0", // 48
66-
"2.3.1", // 49
67-
"3.0.0", // 50 - BREAKING CHANGE
68-
}
13+
var list = []string{
14+
"0.0.1-rc.0", // 0
15+
"0.0.1-rc.0+build", // 1
16+
"0.0.1-rc.1", // 2
17+
"0.0.1", // 3
18+
"0.0.1+build", // 4
19+
"0.0.2-rc.1", // 5 - BREAKING CHANGE
20+
"0.0.2-rc.1+build", // 6
21+
"0.0.2", // 7
22+
"0.0.2+build", // 8
23+
"0.0.3-rc.1", // 9 - BREAKING CHANGE
24+
"0.0.3-rc.2", // 10
25+
"0.0.3", // 11
26+
"0.1.0", // 12 - BREAKING CHANGE
27+
"0.3.3-rc.0", // 13 - BREAKING CHANGE
28+
"0.3.3-rc.1", // 14
29+
"0.3.3", // 15
30+
"0.3.3+build", // 16
31+
"0.3.4-rc.1", // 17
32+
"0.3.4", // 18
33+
"0.4.0", // 19 - BREAKING CHANGE
34+
"1.0.0-rc", // 20 - BREAKING CHANGE
35+
"1.0.0", // 21
36+
"1.0.0+build", // 22
37+
"1.2.1-rc", // 23
38+
"1.2.1", // 24
39+
"1.2.1+build", // 25
40+
"1.2.3-rc.2", // 26
41+
"1.2.3-rc.2+build", // 27
42+
"1.2.3", // 28
43+
"1.2.3+build", // 29
44+
"1.2.4", // 30
45+
"1.3.0-rc.0+build", // 31
46+
"1.3.0", // 32
47+
"1.3.0+build", // 33
48+
"1.3.1-rc.0", // 34
49+
"1.3.1-rc.1", // 35
50+
"1.3.1", // 36
51+
"1.3.5", // 37
52+
"2.0.0-rc", // 38 - BREAKING CHANGE
53+
"2.0.0-rc+build", // 39
54+
"2.0.0", // 40
55+
"2.0.0+build", // 41
56+
"2.1.0-rc", // 42
57+
"2.1.0-rc+build", // 43
58+
"2.1.0", // 44
59+
"2.1.0+build", // 45
60+
"2.1.3-rc", // 46
61+
"2.1.3", // 47
62+
"2.3.0", // 48
63+
"2.3.1", // 49
64+
"3.0.0", // 50 - BREAKING CHANGE
65+
}
6966

67+
func BenchmarkVersionParser(b *testing.B) {
68+
res := &Version{}
7069
for i := 0; i < b.N; i++ {
7170
for _, v := range list {
72-
semver.MustParse(v)
71+
parseInto([]byte(v), res)
7372
}
7473
}
7574

@@ -79,5 +78,32 @@ func BenchmarkVersionParser(b *testing.B) {
7978
// goarch: amd64
8079
// pkg: go.bug.st/relaxed-semver
8180
// cpu: AMD Ryzen 5 3600 6-Core Processor
82-
// BenchmarkVersionParser-12 99518 16150 ns/op 9744 B/op 176 allocs/op
81+
// BenchmarkVersionParser-12 188611 7715 ns/op 8557 B/op 51 allocs/op
82+
}
83+
84+
func BenchmarkVersionComparator(b *testing.B) {
85+
b.StopTimer()
86+
vList := []*Version{}
87+
for _, in := range list {
88+
vList = append(vList, MustParse(in))
89+
}
90+
l := len(vList)
91+
b.StartTimer()
92+
93+
for i := 0; i < b.N; i++ {
94+
// cross compare all versions
95+
for x := 0; x < l; x++ {
96+
for y := 0; y < l; y++ {
97+
vList[x].CompareTo(vList[y])
98+
}
99+
}
100+
}
101+
102+
// Results for v0.11.0:
103+
// $ go test -benchmem -run=^$ -bench ^BenchmarkVersionComparator$ go.bug.st/relaxed-semver -v
104+
// goos: linux
105+
// goarch: amd64
106+
// pkg: go.bug.st/relaxed-semver
107+
// cpu: AMD Ryzen 5 3600 6-Core Processor
108+
// BenchmarkVersionComparator-12 74793 17347 ns/op 0 B/op 0 allocs/op
83109
}

parser.go

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,20 @@ func MustParse(inVersion string) *Version {
2222
}
2323

2424
// Parse parse a version string
25-
func Parse(inVersioin string) (*Version, error) {
25+
func Parse(inVersion string) (*Version, error) {
2626
result := &Version{
2727
major: empty[:],
2828
minor: empty[:],
2929
patch: empty[:],
3030
}
31+
if err := parseInto([]byte(inVersion), result); err != nil {
32+
return nil, err
33+
}
34+
return result, nil
35+
}
3136

37+
func parseInto(in []byte, result *Version) error {
3238
// Setup parsing harness
33-
in := []byte(inVersioin)
3439
inLen := len(in)
3540
currIdx := -1
3641
var curr byte
@@ -51,28 +56,28 @@ func Parse(inVersioin string) (*Version, error) {
5156

5257
// Parse major
5358
if !next() {
54-
return result, nil // empty version
59+
return nil // empty version
5560
}
5661
if !numeric[curr] {
57-
return nil, fmt.Errorf("no major version found")
62+
return fmt.Errorf("no major version found")
5863
}
5964
if curr == '0' {
6065
result.major = in[0:1] // 0
6166
if !next() {
62-
return result, nil
67+
return nil
6368
}
6469
if numeric[curr] {
65-
return nil, fmt.Errorf("major version must not be prefixed with zero")
70+
return fmt.Errorf("major version must not be prefixed with zero")
6671
}
6772
if !versionSeparator[curr] {
68-
return nil, fmt.Errorf("invalid major version separator '%c'", curr)
73+
return fmt.Errorf("invalid major version separator '%c'", curr)
6974
}
7075
// Fallthrough and parse next element
7176
} else {
7277
for {
7378
if !next() {
7479
result.major = in[0:currIdx]
75-
return result, nil
80+
return nil
7681
}
7782
if numeric[curr] {
7883
continue
@@ -81,33 +86,33 @@ func Parse(inVersioin string) (*Version, error) {
8186
result.major = in[0:currIdx]
8287
break
8388
}
84-
return nil, fmt.Errorf("invalid major version separator '%c'", curr)
89+
return fmt.Errorf("invalid major version separator '%c'", curr)
8590
}
8691
}
8792

8893
// Parse minor
8994
if curr == '.' {
9095
if !next() || !numeric[curr] {
91-
return nil, fmt.Errorf("no minor version found")
96+
return fmt.Errorf("no minor version found")
9297
}
9398
if curr == '0' {
9499
result.minor = in[currIdx : currIdx+1] // x.0
95100
if !next() {
96-
return result, nil
101+
return nil
97102
}
98103
if numeric[curr] {
99-
return nil, fmt.Errorf("minor version must not be prefixed with zero")
104+
return fmt.Errorf("minor version must not be prefixed with zero")
100105
}
101106
if !versionSeparator[curr] {
102-
return nil, fmt.Errorf("invalid minor version separator '%c'", curr)
107+
return fmt.Errorf("invalid minor version separator '%c'", curr)
103108
}
104109
// Fallthrough and parse next element
105110
} else {
106111
minorIdx := currIdx
107112
for {
108113
if !next() {
109114
result.minor = in[minorIdx:currIdx]
110-
return result, nil
115+
return nil
111116
}
112117
if numeric[curr] {
113118
continue
@@ -116,34 +121,34 @@ func Parse(inVersioin string) (*Version, error) {
116121
result.minor = in[minorIdx:currIdx]
117122
break
118123
}
119-
return nil, fmt.Errorf("invalid minor version separator '%c'", curr)
124+
return fmt.Errorf("invalid minor version separator '%c'", curr)
120125
}
121126
}
122127
}
123128

124129
// Parse patch
125130
if curr == '.' {
126131
if !next() || !numeric[curr] {
127-
return nil, fmt.Errorf("no patch version found")
132+
return fmt.Errorf("no patch version found")
128133
}
129134
if curr == '0' {
130135
result.patch = in[currIdx : currIdx+1] // x.y.0
131136
if !next() {
132-
return result, nil
137+
return nil
133138
}
134139
if numeric[curr] {
135-
return nil, fmt.Errorf("patch version must not be prefixed with zero")
140+
return fmt.Errorf("patch version must not be prefixed with zero")
136141
}
137142
if !versionSeparator[curr] {
138-
return nil, fmt.Errorf("invalid patch version separator '%c'", curr)
143+
return fmt.Errorf("invalid patch version separator '%c'", curr)
139144
}
140145
// Fallthrough and parse next element
141146
} else {
142147
patchIdx := currIdx
143148
for {
144149
if !next() {
145150
result.patch = in[patchIdx:currIdx]
146-
return result, nil
151+
return nil
147152
}
148153
if numeric[curr] {
149154
continue
@@ -152,7 +157,7 @@ func Parse(inVersioin string) (*Version, error) {
152157
result.patch = in[patchIdx:currIdx]
153158
break
154159
}
155-
return nil, fmt.Errorf("invalid patch version separator '%c'", curr)
160+
return fmt.Errorf("invalid patch version separator '%c'", curr)
156161
}
157162
}
158163
}
@@ -176,15 +181,15 @@ func Parse(inVersioin string) (*Version, error) {
176181
for {
177182
if hasNext := next(); !hasNext || curr == '.' || curr == '+' {
178183
if prereleaseIdx == currIdx {
179-
return nil, fmt.Errorf("empty prerelease not allowed")
184+
return fmt.Errorf("empty prerelease not allowed")
180185
}
181186
if zeroPrefix && !alphaIdentifier && currIdx-prereleaseIdx > 1 {
182-
return nil, fmt.Errorf("numeric prerelease must not be prefixed with zero")
187+
return fmt.Errorf("numeric prerelease must not be prefixed with zero")
183188
}
184189
result.prerelases = append(result.prerelases, in[prereleaseIdx:currIdx])
185190
result.numericPrereleases = append(result.numericPrereleases, !alphaIdentifier)
186191
if !hasNext {
187-
return result, nil
192+
return nil
188193
}
189194
if curr == '+' {
190195
break
@@ -207,7 +212,7 @@ func Parse(inVersioin string) (*Version, error) {
207212
alphaIdentifier = true
208213
continue
209214
}
210-
return nil, fmt.Errorf("invalid prerelease separator: '%c'", curr)
215+
return fmt.Errorf("invalid prerelease separator: '%c'", curr)
211216
}
212217
}
213218

@@ -226,11 +231,11 @@ func Parse(inVersioin string) (*Version, error) {
226231
for {
227232
if hasNext := next(); !hasNext || curr == '.' {
228233
if buildIdx == currIdx {
229-
return nil, fmt.Errorf("empty build tag not allowed")
234+
return fmt.Errorf("empty build tag not allowed")
230235
}
231236
result.builds = append(result.builds, in[buildIdx:currIdx])
232237
if !hasNext {
233-
return result, nil
238+
return nil
234239
}
235240

236241
// Multiple builds
@@ -240,8 +245,8 @@ func Parse(inVersioin string) (*Version, error) {
240245
if identifier[curr] {
241246
continue
242247
}
243-
return nil, fmt.Errorf("invalid separator for builds: '%c'", curr)
248+
return fmt.Errorf("invalid separator for builds: '%c'", curr)
244249
}
245250
}
246-
return nil, fmt.Errorf("invalid separator: '%c'", curr)
251+
return fmt.Errorf("invalid separator: '%c'", curr)
247252
}

0 commit comments

Comments
 (0)