Skip to content
This repository was archived by the owner on Jun 27, 2023. It is now read-only.

Commit d17f086

Browse files
committed
gomock: fix matcher's degenerate quadratic path.
I noticed several tests timing out when attempting to integrate HEAD into our mainline branch. The reason was the accidental introduction of a quadratic call path in callSet.FindMatch, which performed string concatenation in a tight loop. This was corroborated by PProf reporting 23 seconds out of 30 seconds being spent in runtime.concatstrings, which backs onto runtime.memmove. The host I instrumented this on was not the same as the ones for which the data is shown below. The error accumulation routine has been replaced with bytes.Buffer, which exponentially grows its underlying buffer to amortize the cost. After converting a prototype changelist to this pull request you see here, the total runtime of this test that demonstrated the defect fell from 80 seconds to 15. This was just one run on a noisy cluster. This is change introduced the defect: https://github.com/golang/mock/blame/4187d4d04aa043124750c9250259ceafdc5f7380/gomock/callset.go#L68
1 parent 0553d24 commit d17f086

File tree

1 file changed

+5
-4
lines changed

1 file changed

+5
-4
lines changed

gomock/callset.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package gomock
1616

1717
import (
18+
"bytes"
1819
"errors"
1920
"fmt"
2021
)
@@ -65,22 +66,22 @@ func (cs callSet) FindMatch(receiver interface{}, method string, args []interfac
6566

6667
// Search through the unordered set of calls expected on a method on a
6768
// receiver.
68-
callsErrors := ""
69+
var callsErrors bytes.Buffer
6970
for _, call := range calls {
7071
// A call should not normally still be here if exhausted,
7172
// but it can happen if, for instance, .Times(0) was used.
7273
// Pretend the call doesn't match.
7374
if call.exhausted() {
74-
callsErrors += "\nThe call was exhausted."
75+
callsErrors.WriteString("\nThe call was exhausted.")
7576
continue
7677
}
7778
err := call.matches(args)
7879
if err != nil {
79-
callsErrors += "\n" + err.Error()
80+
fmt.Fprintf(&callsErrors, "\n%v", err)
8081
} else {
8182
return call, nil
8283
}
8384
}
8485

85-
return nil, fmt.Errorf(callsErrors)
86+
return nil, fmt.Errorf(callsErrors.String())
8687
}

0 commit comments

Comments
 (0)