Skip to content

Commit 252a666

Browse files
committed
all: delay compiling global regexes and templates
This shaves off about 0.1ms and 2000 allocs from the total init time cost of cmd/cue, which is a small but worthwhile win as measured by benchinit plus benchstat: │ old │ new │ │ sec/op │ sec/op vs base │ CuelangOrgGoCmdCue 1.465m ± 5% 1.363m ± 4% -6.96% (p=0.002 n=6) │ old │ new │ │ B/op │ B/op vs base │ CuelangOrgGoCmdCue 1.253Mi ± 0% 1.095Mi ± 0% -12.58% (p=0.002 n=6) │ old │ new │ │ allocs/op │ allocs/op vs base │ CuelangOrgGoCmdCue 10.275k ± 0% 8.181k ± 0% -20.38% (p=0.002 n=6) Signed-off-by: Daniel Martí <[email protected]> Change-Id: I552116f9e2c1d9c240a21cf044e80961b394c5bb Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1198994 TryBot-Result: CUEcueckoo <[email protected]> Unity-Result: CUE porcuepine <[email protected]> Reviewed-by: Roger Peppe <[email protected]>
1 parent 4c7aecf commit 252a666

File tree

5 files changed

+31
-13
lines changed

5 files changed

+31
-13
lines changed

cue/stats/stats.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package stats
1818

1919
import (
2020
"strings"
21+
"sync"
2122
"text/template"
2223
)
2324

@@ -100,7 +101,8 @@ func (s Counts) Leaks() int64 {
100101
return s.Allocs + s.Reused - s.Freed
101102
}
102103

103-
var stats = template.Must(template.New("stats").Parse(`{{"" -}}
104+
var stats = sync.OnceValue(func() *template.Template {
105+
return template.Must(template.New("stats").Parse(`{{"" -}}
104106
105107
Leaks: {{.Leaks}}
106108
Freed: {{.Freed}}
@@ -111,10 +113,11 @@ Retain: {{.Retained}}
111113
Unifications: {{.Unifications}}
112114
Conjuncts: {{.Conjuncts}}
113115
Disjuncts: {{.Disjuncts}}`))
116+
})
114117

115118
func (s Counts) String() string {
116119
buf := &strings.Builder{}
117-
err := stats.Execute(buf, s)
120+
err := stats().Execute(buf, s)
118121
if err != nil {
119122
panic(err)
120123
}

internal/encoding/yaml/decode.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"regexp"
1010
"strconv"
1111
"strings"
12+
"sync"
1213

1314
"gopkg.in/yaml.v3"
1415

@@ -493,15 +494,17 @@ const (
493494

494495
// rxAnyOctalYaml11 uses the implicit tag resolution regular expression for base-8 integers
495496
// from YAML's 1.1 spec, but including the 8 and 9 digits which aren't valid for octal integers.
496-
var rxAnyOctalYaml11 = regexp.MustCompile(`^[-+]?0[0-9_]+$`)
497+
var rxAnyOctalYaml11 = sync.OnceValue(func() *regexp.Regexp {
498+
return regexp.MustCompile(`^[-+]?0[0-9_]+$`)
499+
})
497500

498501
func (d *decoder) scalar(yn *yaml.Node) (ast.Expr, error) {
499502
tag := yn.ShortTag()
500503
// If the YAML scalar has no explicit tag, yaml.v3 infers a float tag,
501504
// and the value looks like a YAML 1.1 octal literal,
502505
// that means the input value was like `01289` and not a valid octal integer.
503506
// The safest thing to do, and what most YAML decoders do, is to interpret as a string.
504-
if yn.Style&yaml.TaggedStyle == 0 && tag == floatTag && rxAnyOctalYaml11.MatchString(yn.Value) {
507+
if yn.Style&yaml.TaggedStyle == 0 && tag == floatTag && rxAnyOctalYaml11().MatchString(yn.Value) {
505508
tag = strTag
506509
}
507510
switch tag {

internal/encoding/yaml/encode.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"math/big"
2222
"regexp"
2323
"strings"
24+
"sync"
2425

2526
"gopkg.in/yaml.v3"
2627

@@ -159,12 +160,14 @@ func encodeScalar(b *ast.BasicLit) (n *yaml.Node, err error) {
159160
// shouldQuote indicates that a string may be a YAML 1.1. legacy value and that
160161
// the string should be quoted.
161162
func shouldQuote(str string) bool {
162-
return legacyStrings[str] || useQuote.MatchString(str)
163+
return legacyStrings[str] || useQuote().MatchString(str)
163164
}
164165

165166
// This regular expression conservatively matches any date, time string,
166167
// or base60 float.
167-
var useQuote = regexp.MustCompile(`^[\-+0-9:\. \t]+([-:]|[tT])[\-+0-9:\. \t]+[zZ]?$|^0x[a-fA-F0-9]+$`)
168+
var useQuote = sync.OnceValue(func() *regexp.Regexp {
169+
return regexp.MustCompile(`^[\-+0-9:\. \t]+([-:]|[tT])[\-+0-9:\. \t]+[zZ]?$|^0x[a-fA-F0-9]+$`)
170+
})
168171

169172
// legacyStrings contains a map of fixed strings with special meaning for any
170173
// type in the YAML Tag registry (https://yaml.org/type/index.html) as used

mod/module/path.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"regexp"
77
"strings"
8+
"sync"
89
"unicode"
910
"unicode/utf8"
1011

@@ -15,8 +16,12 @@ import (
1516
// The following regular expressions come from https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pulling-manifests
1617
// and ensure that we can store modules inside OCI registries.
1718
var (
18-
basePathPat = regexp.MustCompile(`^[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*(/[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*)*$`)
19-
tagPat = regexp.MustCompile(`^[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}$`)
19+
basePathPat = sync.OnceValue(func() *regexp.Regexp {
20+
return regexp.MustCompile(`^[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*(/[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*)*$`)
21+
})
22+
tagPat = sync.OnceValue(func() *regexp.Regexp {
23+
return regexp.MustCompile(`^[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}$`)
24+
})
2025
)
2126

2227
// Check checks that a given module path, version pair is valid.
@@ -128,7 +133,7 @@ func CheckPathWithoutVersion(basePath string) (err error) {
128133
}
129134
}
130135
// Sanity check agreement with OCI specs.
131-
if !basePathPat.MatchString(basePath) {
136+
if !basePathPat().MatchString(basePath) {
132137
return fmt.Errorf("path does not conform to OCI repository name restrictions; see https://github.com/opencontainers/distribution-spec/blob/HEAD/spec.md#pulling-manifests")
133138
}
134139
return nil
@@ -165,7 +170,7 @@ func CheckPath(mpath string) (err error) {
165170
if semver.Major(vers) != vers {
166171
return fmt.Errorf("path can contain major version only")
167172
}
168-
if !tagPat.MatchString(vers) {
173+
if !tagPat().MatchString(vers) {
169174
return fmt.Errorf("non-conforming version %q", vers)
170175
}
171176
} else {

pkg/uuid/uuid.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,20 @@ import (
2121
"fmt"
2222
"math/big"
2323
"regexp"
24+
"sync"
2425

2526
"github.com/google/uuid"
2627
)
2728

28-
var valid = regexp.MustCompile(
29-
"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")
29+
var valid = sync.OnceValue(func() *regexp.Regexp {
30+
return regexp.MustCompile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")
31+
})
32+
33+
// TODO(mvdan): should Valid not use [uuid.Validate]?
3034

3135
// Valid can be used to define a valid Valid.
3236
func Valid(s string) error {
33-
if !valid.MatchString(string(s)) {
37+
if !valid().MatchString(string(s)) {
3438
return fmt.Errorf("invalid UUID %q", s)
3539
}
3640
return nil

0 commit comments

Comments
 (0)