Permalink
Browse files

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: Brad Fitzpatrick <bradfitz@golang.org>
  • Loading branch information...
haya14busa authored and bradfitz committed Mar 16, 2017
1 parent 9e6b79a commit 8a16d7d40a371e61f6d30604224039cf9a46d106
Showing with 33 additions and 2 deletions.
  1. +4 −2 src/regexp/exec.go
  2. +29 −0 src/regexp/exec_test.go
View
@@ -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.
// It reports whether a match was found.
// 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
if startCond == ^syntax.EmptyOp(0) { // impossible
return false
}
m.matched = false
+ m.matchcap = m.matchcap[:ncap]
for i := range m.matchcap {
m.matchcap[i] = -1
}
@@ -428,7 +430,7 @@ func (re *Regexp) doExecute(r io.RuneReader, b []byte, s string, pos int, ncap i
size = len(s)
}
if m.op != notOnePass {
- if !m.onepass(i, pos) {
+ if !m.onepass(i, pos, ncap) {
re.put(m)
return nil
}
View
@@ -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 }{
{"Easy0", "ABCDEFGHIJKLMNOPQRSTUVWXYZ$"},
{"Easy0i", "(?i)ABCDEFGHIJklmnopqrstuvwxyz$"},

0 comments on commit 8a16d7d

Please sign in to comment.