mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/compile: optimize map-clearing range idiom
replace map clears of the form:
for k := range m {
delete(m, k)
}
(where m is map with key type that is reflexive for ==)
with a new runtime function that clears the maps backing
array with a memclr and reinitializes the hmap struct.
Map key types that for example contain floats are not
replaced by this optimization since NaN keys cannot
be deleted from maps using delete.
name old time/op new time/op delta
GoMapClear/Reflexive/1 92.2ns ± 1% 47.1ns ± 2% -48.89% (p=0.000 n=9+9)
GoMapClear/Reflexive/10 108ns ± 1% 48ns ± 2% -55.68% (p=0.000 n=10+10)
GoMapClear/Reflexive/100 303ns ± 2% 110ns ± 3% -63.56% (p=0.000 n=10+10)
GoMapClear/Reflexive/1000 3.58µs ± 3% 1.23µs ± 2% -65.49% (p=0.000 n=9+10)
GoMapClear/Reflexive/10000 28.2µs ± 3% 10.3µs ± 2% -63.55% (p=0.000 n=9+10)
GoMapClear/NonReflexive/1 121ns ± 2% 124ns ± 7% ~ (p=0.097 n=10+10)
GoMapClear/NonReflexive/10 137ns ± 2% 139ns ± 3% +1.53% (p=0.033 n=10+10)
GoMapClear/NonReflexive/100 331ns ± 3% 334ns ± 2% ~ (p=0.342 n=10+10)
GoMapClear/NonReflexive/1000 3.64µs ± 3% 3.64µs ± 2% ~ (p=0.887 n=9+10)
GoMapClear/NonReflexive/10000 28.1µs ± 2% 28.4µs ± 3% ~ (p=0.247 n=10+10)
Fixes #20138
Change-Id: I181332a8ef434a4f0d89659f492d8711db3f3213
Reviewed-on: https://go-review.googlesource.com/110055
Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
parent
cd1976dbef
commit
aee71dd70b
8 changed files with 437 additions and 110 deletions
|
|
@ -154,6 +154,14 @@ func cheapComputableIndex(width int64) bool {
|
|||
// Node n may also be modified in place, and may also be
|
||||
// the returned node.
|
||||
func walkrange(n *Node) *Node {
|
||||
if isMapClear(n) {
|
||||
m := n.Right
|
||||
lno := setlineno(m)
|
||||
n = mapClear(m)
|
||||
lineno = lno
|
||||
return n
|
||||
}
|
||||
|
||||
// variable name conventions:
|
||||
// ohv1, hv1, hv2: hidden (old) val 1, 2
|
||||
// ha, hit: hidden aggregate, iterator
|
||||
|
|
@ -449,6 +457,69 @@ func walkrange(n *Node) *Node {
|
|||
return n
|
||||
}
|
||||
|
||||
// isMapClear checks if n is of the form:
|
||||
//
|
||||
// for k := range m {
|
||||
// delete(m, k)
|
||||
// }
|
||||
//
|
||||
// where == for keys of map m is reflexive.
|
||||
func isMapClear(n *Node) bool {
|
||||
if Debug['N'] != 0 || instrumenting {
|
||||
return false
|
||||
}
|
||||
|
||||
if n.Op != ORANGE || n.Type.Etype != TMAP || n.List.Len() != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
k := n.List.First()
|
||||
if k == nil || k.isBlank() {
|
||||
return false
|
||||
}
|
||||
|
||||
// Require k to be a new variable name.
|
||||
if k.Name == nil || k.Name.Defn != n {
|
||||
return false
|
||||
}
|
||||
|
||||
if n.Nbody.Len() != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
stmt := n.Nbody.First() // only stmt in body
|
||||
if stmt == nil || stmt.Op != ODELETE {
|
||||
return false
|
||||
}
|
||||
|
||||
m := n.Right
|
||||
if !samesafeexpr(stmt.List.First(), m) || !samesafeexpr(stmt.List.Second(), k) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Keys where equality is not reflexive can not be deleted from maps.
|
||||
if !isreflexive(m.Type.Key()) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// mapClear constructs a call to runtime.mapclear for the map m.
|
||||
func mapClear(m *Node) *Node {
|
||||
t := m.Type
|
||||
|
||||
// instantiate mapclear(typ *type, hmap map[any]any)
|
||||
fn := syslook("mapclear")
|
||||
fn = substArgTypes(fn, t.Key(), t.Elem())
|
||||
n := mkcall1(fn, nil, nil, typename(t), m)
|
||||
|
||||
n = typecheck(n, Etop)
|
||||
n = walkstmt(n)
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
// Lower n into runtime·memclr if possible, for
|
||||
// fast zeroing of slices and arrays (issue 5373).
|
||||
// Look for instances of
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue