language: fix performance bugs
- Match should not allocate
- Prevent allocation by not including a string in an invalid Extension.
Change-Id: Icfe091121d99945b70084214ae9e76c79d6ec5b8
Reviewed-on: https://go-review.googlesource.com/30271
Run-TryBot: Marcel van Lohuizen <mpvl@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/language/language.go b/language/language.go
index 3c19041..5eecceb 100644
--- a/language/language.go
+++ b/language/language.go
@@ -593,7 +593,7 @@
return Extension{ext}, true
}
}
- return Extension{string(x)}, false
+ return Extension{}, false
}
// Extensions returns all extensions of t.
diff --git a/language/match.go b/language/match.go
index eec72bc..8ad9505 100644
--- a/language/match.go
+++ b/language/match.go
@@ -396,8 +396,8 @@
// matchHeader has the lists of tags for exact matches and matches based on
// maximized and canonicalized tags for a given language.
type matchHeader struct {
- exact []haveTag
- max []haveTag
+ exact []*haveTag
+ max []*haveTag
}
// haveTag holds a supported Tag and its maximized script and region. The maximized
@@ -457,7 +457,7 @@
}
}
if exact {
- h.exact = append(h.exact, n)
+ h.exact = append(h.exact, &n)
}
// Allow duplicate maximized tags, but create a linked list to allow quickly
// comparing the equivalents and bail out.
@@ -472,7 +472,7 @@
break
}
}
- h.max = append(h.max, n)
+ h.max = append(h.max, &n)
}
// header returns the matchHeader for the given language. It creates one if
@@ -503,7 +503,7 @@
pair, _ := makeHaveTag(tag, i)
m.header(tag.lang).addIfNew(pair, true)
}
- m.default_ = &m.header(supported[0].lang).exact[0]
+ m.default_ = m.header(supported[0].lang).exact[0]
for i, tag := range supported {
pair, max := makeHaveTag(tag, i)
if max != tag.lang {
@@ -520,7 +520,8 @@
return
}
hw := m.header(langID(want))
- for _, v := range hh.max {
+ for _, ht := range hh.max {
+ v := *ht
if conf < v.conf {
v.conf = conf
}
@@ -580,7 +581,7 @@
continue
}
for i := range h.exact {
- have := &h.exact[i]
+ have := h.exact[i]
if have.tag.equalsRest(w) {
return have, w, Exact
}
@@ -591,7 +592,7 @@
// Base language is not defined.
if h != nil {
for i := range h.exact {
- have := &h.exact[i]
+ have := h.exact[i]
if have.tag.equalsRest(w) {
return have, w, Exact
}
@@ -609,11 +610,11 @@
}
// Check for match based on maximized tag.
for i := range h.max {
- have := &h.max[i]
+ have := h.max[i]
best.update(have, w, max.script, max.region)
if best.conf == Exact {
for have.nextMax != 0 {
- have = &h.max[have.nextMax]
+ have = h.max[have.nextMax]
best.update(have, w, max.script, max.region)
}
return best.have, best.want, High
diff --git a/language/match_test.go b/language/match_test.go
index 57b1644..9d87a09 100644
--- a/language/match_test.go
+++ b/language/match_test.go
@@ -10,6 +10,8 @@
"fmt"
"strings"
"testing"
+
+ "golang.org/x/text/internal/testtext"
)
var verbose = flag.Bool("verbose", false, "set to true to print the internal tables of matchers")
@@ -246,22 +248,23 @@
return fmt.Sprintf("%v:%d:%v:%v-%v|%v", t.tag, t.index, t.conf, t.maxRegion, t.maxScript, t.altScript)
}
+func parseSupported(list string) (out []Tag) {
+ for _, s := range strings.Split(list, ",") {
+ out = append(out, mk(strings.TrimSpace(s)))
+ }
+ return out
+}
+
// The test set for TestBestMatch is defined in data_test.go.
func TestBestMatch(t *testing.T) {
- parse := func(list string) (out []Tag) {
- for _, s := range strings.Split(list, ",") {
- out = append(out, mk(strings.TrimSpace(s)))
- }
- return out
- }
for i, tt := range matchTests {
- supported := parse(tt.supported)
+ supported := parseSupported(tt.supported)
m := newMatcher(supported)
if *verbose {
fmt.Printf("%s:\n%v\n", tt.comment, m)
}
for _, tm := range tt.test {
- tag, _, conf := m.Match(parse(tm.desired)...)
+ tag, _, conf := m.Match(parseSupported(tm.desired)...)
if tag.String() != tm.match {
t.Errorf("%d:%s: find %s in %q: have %s; want %s (%v)\n", i, tt.comment, tm.desired, tt.supported, tag, tm.match, conf)
}
@@ -269,6 +272,18 @@
}
}
+func TestBestMatchAlloc(t *testing.T) {
+ m := NewMatcher(parseSupported("en sr nl"))
+ // Go allocates when creating a list of tags from a single tag!
+ list := []Tag{English}
+ avg := testtext.AllocsPerRun(1, func() {
+ m.Match(list...)
+ })
+ if avg > 0 {
+ t.Errorf("got %f; want 0", avg)
+ }
+}
+
var benchHave = []Tag{
mk("en"),
mk("en-GB"),