Commit 8a16d7d4 authored by haya14busa's avatar haya14busa Committed by Brad Fitzpatrick

regexp: reduce allocs in regexp.Match for onepass regex

There were no allocations in regexp.Match for *non* onepass regex
because m.matchcap length is reset to zero (ncap=0 for regexp.Match).

But, as for onepass regex, m.matchcap length remains as it is even when
ncap=0 and it leads needless allocations.

benchmark                                    old ns/op      new ns/op      delta
BenchmarkMatch_onepass_regex/32-4      6465           4628           -28.41%
BenchmarkMatch_onepass_regex/1K-4      208324         151558         -27.25%
BenchmarkMatch_onepass_regex/32K-4     7230259        5834492        -19.30%
BenchmarkMatch_onepass_regex/1M-4      234379810      166310682      -29.04%
BenchmarkMatch_onepass_regex/32M-4     7903529363     4981119950     -36.98%

benchmark                                    old MB/s     new MB/s     speedup
BenchmarkMatch_onepass_regex/32-4      4.95         6.91         1.40x
BenchmarkMatch_onepass_regex/1K-4      4.92         6.76         1.37x
BenchmarkMatch_onepass_regex/32K-4     4.53         5.62         1.24x
BenchmarkMatch_onepass_regex/1M-4      4.47         6.30         1.41x
BenchmarkMatch_onepass_regex/32M-4     4.25         6.74         1.59x

benchmark                                    old allocs     new allocs     delta
BenchmarkMatch_onepass_regex/32-4      32             0              -100.00%
BenchmarkMatch_onepass_regex/1K-4      1024           0              -100.00%
BenchmarkMatch_onepass_regex/32K-4     32768          0              -100.00%
BenchmarkMatch_onepass_regex/1M-4      1048576        0              -100.00%
BenchmarkMatch_onepass_regex/32M-4     104559255      0              -100.00%

benchmark                                    old bytes      new bytes     delta
BenchmarkMatch_onepass_regex/32-4      512            0             -100.00%
BenchmarkMatch_onepass_regex/1K-4      16384          0             -100.00%
BenchmarkMatch_onepass_regex/32K-4     524288         0             -100.00%
BenchmarkMatch_onepass_regex/1M-4      16777216       0             -100.00%
BenchmarkMatch_onepass_regex/32M-4     2019458128     0             -100.00%

Fixes #19573

Change-Id: I033982d0003ebb0360bb40b92eb3941c781ec74d
Reviewed-on: https://go-review.googlesource.com/38270
Run-TryBot: Michael Matloob <matloob@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 9e6b79a5
...@@ -309,12 +309,14 @@ func (m *machine) add(q *queue, pc uint32, pos int, cap []int, cond syntax.Empty ...@@ -309,12 +309,14 @@ func (m *machine) add(q *queue, pc uint32, pos int, cap []int, cond syntax.Empty
// onepass runs the machine over the input starting at pos. // onepass runs the machine over the input starting at pos.
// It reports whether a match was found. // It reports whether a match was found.
// If so, m.matchcap holds the submatch information. // If so, m.matchcap holds the submatch information.
func (m *machine) onepass(i input, pos int) bool { // ncap is the number of captures.
func (m *machine) onepass(i input, pos, ncap int) bool {
startCond := m.re.cond startCond := m.re.cond
if startCond == ^syntax.EmptyOp(0) { // impossible if startCond == ^syntax.EmptyOp(0) { // impossible
return false return false
} }
m.matched = false m.matched = false
m.matchcap = m.matchcap[:ncap]
for i := range m.matchcap { for i := range m.matchcap {
m.matchcap[i] = -1 m.matchcap[i] = -1
} }
...@@ -428,7 +430,7 @@ func (re *Regexp) doExecute(r io.RuneReader, b []byte, s string, pos int, ncap i ...@@ -428,7 +430,7 @@ func (re *Regexp) doExecute(r io.RuneReader, b []byte, s string, pos int, ncap i
size = len(s) size = len(s)
} }
if m.op != notOnePass { if m.op != notOnePass {
if !m.onepass(i, pos) { if !m.onepass(i, pos, ncap) {
re.put(m) re.put(m)
return nil return nil
} }
......
...@@ -681,6 +681,35 @@ func BenchmarkMatch(b *testing.B) { ...@@ -681,6 +681,35 @@ func BenchmarkMatch(b *testing.B) {
} }
} }
func BenchmarkMatch_onepass_regex(b *testing.B) {
isRaceBuilder := strings.HasSuffix(testenv.Builder(), "-race")
r := MustCompile(`(?s)\A.*\z`)
if r.get().op == notOnePass {
b.Fatalf("want onepass regex, but %q is not onepass", r)
}
for _, size := range benchSizes {
if isRaceBuilder && size.n > 1<<10 {
continue
}
t := makeText(size.n)
bs := make([][]byte, len(t))
for i, s := range t {
bs[i] = []byte{s}
}
b.Run(size.name, func(b *testing.B) {
b.SetBytes(int64(size.n))
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for _, byts := range bs {
if !r.Match(byts) {
b.Fatal("not match!")
}
}
}
})
}
}
var benchData = []struct{ name, re string }{ var benchData = []struct{ name, re string }{
{"Easy0", "ABCDEFGHIJKLMNOPQRSTUVWXYZ$"}, {"Easy0", "ABCDEFGHIJKLMNOPQRSTUVWXYZ$"},
{"Easy0i", "(?i)ABCDEFGHIJklmnopqrstuvwxyz$"}, {"Easy0i", "(?i)ABCDEFGHIJklmnopqrstuvwxyz$"},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment