Skip to content

Commit

Permalink
all: delay compiling global regexes and templates
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
mvdan committed Aug 7, 2024
1 parent 4c7aecf commit 252a666
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 13 deletions.
7 changes: 5 additions & 2 deletions cue/stats/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package stats

import (
"strings"
"sync"
"text/template"
)

Expand Down Expand Up @@ -100,7 +101,8 @@ func (s Counts) Leaks() int64 {
return s.Allocs + s.Reused - s.Freed
}

var stats = template.Must(template.New("stats").Parse(`{{"" -}}
var stats = sync.OnceValue(func() *template.Template {
return template.Must(template.New("stats").Parse(`{{"" -}}
Leaks: {{.Leaks}}
Freed: {{.Freed}}
Expand All @@ -111,10 +113,11 @@ Retain: {{.Retained}}
Unifications: {{.Unifications}}
Conjuncts: {{.Conjuncts}}
Disjuncts: {{.Disjuncts}}`))
})

func (s Counts) String() string {
buf := &strings.Builder{}
err := stats.Execute(buf, s)
err := stats().Execute(buf, s)
if err != nil {
panic(err)
}
Expand Down
7 changes: 5 additions & 2 deletions internal/encoding/yaml/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"regexp"
"strconv"
"strings"
"sync"

"gopkg.in/yaml.v3"

Expand Down Expand Up @@ -493,15 +494,17 @@ const (

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

func (d *decoder) scalar(yn *yaml.Node) (ast.Expr, error) {
tag := yn.ShortTag()
// If the YAML scalar has no explicit tag, yaml.v3 infers a float tag,
// and the value looks like a YAML 1.1 octal literal,
// that means the input value was like `01289` and not a valid octal integer.
// The safest thing to do, and what most YAML decoders do, is to interpret as a string.
if yn.Style&yaml.TaggedStyle == 0 && tag == floatTag && rxAnyOctalYaml11.MatchString(yn.Value) {
if yn.Style&yaml.TaggedStyle == 0 && tag == floatTag && rxAnyOctalYaml11().MatchString(yn.Value) {
tag = strTag
}
switch tag {
Expand Down
7 changes: 5 additions & 2 deletions internal/encoding/yaml/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"math/big"
"regexp"
"strings"
"sync"

"gopkg.in/yaml.v3"

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

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

// legacyStrings contains a map of fixed strings with special meaning for any
// type in the YAML Tag registry (https://yaml.org/type/index.html) as used
Expand Down
13 changes: 9 additions & 4 deletions mod/module/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"regexp"
"strings"
"sync"
"unicode"
"unicode/utf8"

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

// Check checks that a given module path, version pair is valid.
Expand Down Expand Up @@ -128,7 +133,7 @@ func CheckPathWithoutVersion(basePath string) (err error) {
}
}
// Sanity check agreement with OCI specs.
if !basePathPat.MatchString(basePath) {
if !basePathPat().MatchString(basePath) {
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")
}
return nil
Expand Down Expand Up @@ -165,7 +170,7 @@ func CheckPath(mpath string) (err error) {
if semver.Major(vers) != vers {
return fmt.Errorf("path can contain major version only")
}
if !tagPat.MatchString(vers) {
if !tagPat().MatchString(vers) {
return fmt.Errorf("non-conforming version %q", vers)
}
} else {
Expand Down
10 changes: 7 additions & 3 deletions pkg/uuid/uuid.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,20 @@ import (
"fmt"
"math/big"
"regexp"
"sync"

"github.com/google/uuid"
)

var valid = regexp.MustCompile(
"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")
var valid = sync.OnceValue(func() *regexp.Regexp {
return regexp.MustCompile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")
})

// TODO(mvdan): should Valid not use [uuid.Validate]?

// Valid can be used to define a valid Valid.
func Valid(s string) error {
if !valid.MatchString(string(s)) {
if !valid().MatchString(string(s)) {
return fmt.Errorf("invalid UUID %q", s)
}
return nil
Expand Down

0 comments on commit 252a666

Please sign in to comment.