]> Cypherpunks repositories - gostls13.git/commitdiff
regexp: reduce allocs in regexp.Match for onepass regex
authorhaya14busa <haya14busa@gmail.com>
Thu, 16 Mar 2017 15:41:58 +0000 (00:41 +0900)
committerBrad Fitzpatrick <bradfitz@golang.org>
Thu, 23 Mar 2017 00:02:55 +0000 (00:02 +0000)
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>
src/regexp/exec.go
src/regexp/exec_test.go

index 977619cb28ae0024f20f14616523a7f6fb8a4941..f8fe7b5defffbe4ccd03287d7bc1277bac503ef6 100644 (file)
@@ -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
                }
index 766394de6ee70d3677c2b4fc01a0174ce767a343..5f8e747b17bc1f447722fee9c609003541d4e489 100644 (file)
@@ -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$"},