зеркало из https://github.com/golang/text.git
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:
Родитель
aeb47ccdcf
Коммит
2646850675
|
@ -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)
|
||||
}
|
||||
|
||||
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 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"),
|
||||
|
|
Загрузка…
Ссылка в новой задаче