The winnow() "Not" path routed through grep(), which called
newSingleSelection() for every node, allocating a Selection and
a 1-element []*html.Node slice on the heap per node, only to
immediately unwrap it via s.Get(0) for m.Match().
This commit call m.Match(n) directly and pre-allocate the result slice.
It also adds BenchmarkNotMatcher to isolate the winnow path from
compileMatcher overhead.
Here are my local benchmark results, for BenchmarkNotMatcher with 373 <li>
nodes, and a pre-compiled matcher:
old ns/op new ns/op delta
22750 19350 ~-15%
old B/op new B/op delta
9384 3120 -66.7%
old allocs new allocs delta
11 2 -81.8%