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>
// 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
}
size = len(s)
}
if m.op != notOnePass {
- if !m.onepass(i, pos) {
+ if !m.onepass(i, pos, ncap) {
re.put(m)
return nil
}
}
}
+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$"},