mirror of
https://github.com/PuerkitoBio/goquery.git
synced 2026-10-19 16:15:33 +00:00
perf: presize the result slice in Empty()
Empty() collected the removed children into a nil slice grown via append,
forcing a series of grow-and-double reallocations proportional to the number
of children.
This commit walks the children once up front to count them, then allocate the
result slice at its final capacity before the removal loop.
The count pass is a cheap, branch-predicted pointer walk that replaces several
slice reallocations with a single allocation.
benchstat (-benchtime=2000x, count=10):
| old | new |
| allocs | allocs vs base |
Empty10-8 | 3.000 | 2.000 -33.33% |
Empty100-8 | 6.000 | 2.000 -66.67% |
Empty1000-8 | 9.000 | 2.000 -77.78% |
B/op: -47% / -56% / -53%
sec/op: Empty10 -53%, Empty100 -58%, Empty1000 ~ (noise)
This commit is contained in:
+9
-1
@@ -166,8 +166,16 @@ func (s *Selection) Clone() *Selection {
|
||||
// Empty removes all children nodes from the set of matched elements.
|
||||
// It returns the children nodes in a new Selection.
|
||||
func (s *Selection) Empty() *Selection {
|
||||
var nodes []*html.Node
|
||||
// Count the children first so nodes can be presized, avoiding append's
|
||||
// repeated reallocations.
|
||||
count := 0
|
||||
for _, n := range s.Nodes {
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
nodes := make([]*html.Node, 0, count)
|
||||
for _, n := range s.Nodes {
|
||||
for c := n.FirstChild; c != nil; c = n.FirstChild {
|
||||
n.RemoveChild(c)
|
||||
|
||||
Reference in New Issue
Block a user