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>
This commit is contained in:
Marcel van Lohuizen 2016-10-04 11:10:07 +02:00
Родитель aeb47ccdcf
Коммит 2646850675
3 изменённых файлов: 35 добавлений и 19 удалений

Просмотреть файл

@ -593,7 +593,7 @@ func (t Tag) Extension(x byte) (ext Extension, ok bool) {
return Extension{ext}, true
}
}
return Extension{string(x)}, false
return Extension{}, false
}
// Extensions returns all extensions of t.

Просмотреть файл

@ -396,8 +396,8 @@ type matcher struct {
// 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 @@ func (h *matchHeader) addIfNew(n haveTag, exact bool) {
}
}
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 @@ func (h *matchHeader) addIfNew(n haveTag, exact bool) {
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 @@ func newMatcher(supported []Tag) *matcher {
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 @@ func newMatcher(supported []Tag) *matcher {
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 @@ func (m *matcher) getBest(want ...Tag) (got *haveTag, orig Tag, c Confidence) {
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 @@ func (m *matcher) getBest(want ...Tag) (got *haveTag, orig Tag, c Confidence) {
// 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 @@ func (m *matcher) getBest(want ...Tag) (got *haveTag, orig Tag, c Confidence) {
}
// 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

Просмотреть файл

@ -10,6 +10,8 @@ import (
"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 @@ func (t haveTag) String() string {
return fmt.Sprintf("%v:%d:%v:%v-%v|%v", t.tag, t.index, t.conf, t.maxRegion, t.maxScript, t.altScript)
}
// The test set for TestBestMatch is defined in data_test.go.
func TestBestMatch(t *testing.T) {
parse := func(list string) (out []Tag) {
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) {
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 TestBestMatch(t *testing.T) {
}
}
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"),