Skip to content

Commit 3b90e4b

Browse files
committed
Benchmarking and performance improvements
1 parent 2286627 commit 3b90e4b

3 files changed

Lines changed: 183 additions & 1 deletion

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.test
2+
*.prof

bench_test.go

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
package vers
2+
3+
import "testing"
4+
5+
// Parsing benchmarks
6+
7+
func BenchmarkParse_VersURI_Simple(b *testing.B) {
8+
for b.Loop() {
9+
Parse("vers:npm/>=1.2.3")
10+
}
11+
}
12+
13+
func BenchmarkParse_VersURI_Complex(b *testing.B) {
14+
for b.Loop() {
15+
Parse("vers:npm/>=1.2.3|<2.0.0|!=1.5.0")
16+
}
17+
}
18+
19+
func BenchmarkParseNative_Npm_Caret(b *testing.B) {
20+
for b.Loop() {
21+
ParseNative("^1.2.3", "npm")
22+
}
23+
}
24+
25+
func BenchmarkParseNative_Npm_Tilde(b *testing.B) {
26+
for b.Loop() {
27+
ParseNative("~1.2.3", "npm")
28+
}
29+
}
30+
31+
func BenchmarkParseNative_Npm_Range(b *testing.B) {
32+
for b.Loop() {
33+
ParseNative(">=1.0.0 <2.0.0", "npm")
34+
}
35+
}
36+
37+
func BenchmarkParseNative_Npm_Or(b *testing.B) {
38+
for b.Loop() {
39+
ParseNative("^1.0.0 || ^2.0.0 || ^3.0.0", "npm")
40+
}
41+
}
42+
43+
func BenchmarkParseNative_Gem_Pessimistic(b *testing.B) {
44+
for b.Loop() {
45+
ParseNative("~> 1.2.3", "gem")
46+
}
47+
}
48+
49+
func BenchmarkParseNative_Pypi_Compatible(b *testing.B) {
50+
for b.Loop() {
51+
ParseNative("~=1.4.2", "pypi")
52+
}
53+
}
54+
55+
func BenchmarkParseNative_Maven_Bracket(b *testing.B) {
56+
for b.Loop() {
57+
ParseNative("[1.0,2.0)", "maven")
58+
}
59+
}
60+
61+
// Contains benchmarks
62+
63+
func BenchmarkContains_Simple(b *testing.B) {
64+
r, _ := ParseNative("^1.2.3", "npm")
65+
b.ResetTimer()
66+
for b.Loop() {
67+
r.Contains("1.5.0")
68+
}
69+
}
70+
71+
func BenchmarkContains_MultiInterval(b *testing.B) {
72+
r, _ := ParseNative("^1.0.0 || ^2.0.0 || ^3.0.0", "npm")
73+
b.ResetTimer()
74+
for b.Loop() {
75+
r.Contains("2.5.0")
76+
}
77+
}
78+
79+
func BenchmarkContains_WithExclusions(b *testing.B) {
80+
r, _ := Parse("vers:npm/>=1.0.0|<2.0.0|!=1.5.0|!=1.6.0|!=1.7.0")
81+
b.ResetTimer()
82+
for b.Loop() {
83+
r.Contains("1.8.0")
84+
}
85+
}
86+
87+
func BenchmarkContains_Prerelease(b *testing.B) {
88+
r, _ := ParseNative(">=1.0.0-alpha.1", "npm")
89+
b.ResetTimer()
90+
for b.Loop() {
91+
r.Contains("1.0.0-beta.2")
92+
}
93+
}
94+
95+
func BenchmarkCompare_Simple(b *testing.B) {
96+
for b.Loop() {
97+
Compare("1.2.3", "1.2.4")
98+
}
99+
}
100+
101+
func BenchmarkCompare_Prerelease(b *testing.B) {
102+
for b.Loop() {
103+
Compare("1.0.0-alpha.1", "1.0.0-beta.2")
104+
}
105+
}
106+
107+
// Range operation benchmarks
108+
109+
func BenchmarkUnion_TwoRanges(b *testing.B) {
110+
r1, _ := ParseNative("^1.0.0", "npm")
111+
r2, _ := ParseNative("^2.0.0", "npm")
112+
b.ResetTimer()
113+
for b.Loop() {
114+
r1.Union(r2)
115+
}
116+
}
117+
118+
func BenchmarkUnion_ManyRanges(b *testing.B) {
119+
ranges := make([]*Range, 10)
120+
for i := range ranges {
121+
ranges[i], _ = ParseNative("^1.0.0", "npm")
122+
}
123+
b.ResetTimer()
124+
for b.Loop() {
125+
result := ranges[0]
126+
for _, r := range ranges[1:] {
127+
result = result.Union(r)
128+
}
129+
}
130+
}
131+
132+
func BenchmarkIntersect_TwoRanges(b *testing.B) {
133+
r1, _ := ParseNative(">=1.0.0", "npm")
134+
r2, _ := ParseNative("<2.0.0", "npm")
135+
b.ResetTimer()
136+
for b.Loop() {
137+
r1.Intersect(r2)
138+
}
139+
}
140+
141+
func BenchmarkIntersect_ManyRanges(b *testing.B) {
142+
r1, _ := ParseNative(">=1.0.0", "npm")
143+
r2, _ := ParseNative("<3.0.0", "npm")
144+
r3, _ := ParseNative(">=1.5.0", "npm")
145+
r4, _ := ParseNative("<2.5.0", "npm")
146+
b.ResetTimer()
147+
for b.Loop() {
148+
r1.Intersect(r2).Intersect(r3).Intersect(r4)
149+
}
150+
}
151+
152+
// Satisfies benchmarks (combines parsing and contains)
153+
154+
func BenchmarkSatisfies_VersURI(b *testing.B) {
155+
for b.Loop() {
156+
Satisfies("1.5.0", "vers:npm/>=1.0.0|<2.0.0", "")
157+
}
158+
}
159+
160+
func BenchmarkSatisfies_Native(b *testing.B) {
161+
for b.Loop() {
162+
Satisfies("1.5.0", "^1.2.3", "npm")
163+
}
164+
}

version.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,18 @@ import (
55
"regexp"
66
"strconv"
77
"strings"
8+
"sync"
89
)
910

1011
// SemanticVersionRegex matches semantic version strings.
1112
var SemanticVersionRegex = regexp.MustCompile(`^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([^+]+))?(?:\+(.+))?$`)
1213

14+
// simpleNumericRegex matches simple numeric versions like "1" or "42".
15+
var simpleNumericRegex = regexp.MustCompile(`^\d+$`)
16+
17+
// versionCache caches parsed versions to avoid re-parsing the same strings.
18+
var versionCache sync.Map
19+
1320
// VersionInfo represents a parsed version with its components.
1421
type VersionInfo struct {
1522
Major int
@@ -26,12 +33,18 @@ func ParseVersion(s string) (*VersionInfo, error) {
2633
return nil, fmt.Errorf("empty version string")
2734
}
2835

36+
// Check cache first
37+
if cached, ok := versionCache.Load(s); ok {
38+
return cached.(*VersionInfo), nil
39+
}
40+
2941
v := &VersionInfo{Original: s}
3042

3143
// Handle simple numeric versions
32-
if match, _ := regexp.MatchString(`^\d+$`, s); match {
44+
if simpleNumericRegex.MatchString(s) {
3345
major, _ := strconv.Atoi(s)
3446
v.Major = major
47+
versionCache.Store(s, v)
3548
return v, nil
3649
}
3750

@@ -48,6 +61,7 @@ func ParseVersion(s string) (*VersionInfo, error) {
4861
}
4962
v.Prerelease = matches[4]
5063
v.Build = matches[5]
64+
versionCache.Store(s, v)
5165
return v, nil
5266
}
5367

@@ -74,6 +88,7 @@ func ParseVersion(s string) (*VersionInfo, error) {
7488
if len(parts) > 3 && v.Prerelease == "" {
7589
v.Prerelease = strings.Join(parts[3:], ".")
7690
}
91+
versionCache.Store(s, v)
7792
return v, nil
7893
}
7994

@@ -84,6 +99,7 @@ func ParseVersion(s string) (*VersionInfo, error) {
8499
if len(parts) > 1 {
85100
v.Prerelease = parts[1]
86101
}
102+
versionCache.Store(s, v)
87103
return v, nil
88104
}
89105

0 commit comments

Comments
 (0)