go/test/mapclear.go
Martin Möhrmann aee71dd70b 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>
2018-05-08 21:15:16 +00:00

90 lines
1.5 KiB
Go

// run
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Ensure that range loops over maps with delete statements
// have the requisite side-effects.
package main
import (
"fmt"
"os"
)
func checkcleared() {
m := make(map[byte]int)
m[1] = 1
m[2] = 2
for k := range m {
delete(m, k)
}
l := len(m)
if want := 0; l != want {
fmt.Printf("len after map clear = %d want %d\n", l, want)
os.Exit(1)
}
m[0] = 0 // To have non empty map and avoid internal map code fast paths.
n := 0
for range m {
n++
}
if want := 1; n != want {
fmt.Printf("number of keys found = %d want %d\n", n, want)
os.Exit(1)
}
}
func checkloopvars() {
k := 0
m := make(map[int]int)
m[42] = 0
for k = range m {
delete(m, k)
}
if want := 42; k != want {
fmt.Printf("var after range with side-effect = %d want %d\n", k, want)
os.Exit(1)
}
}
func checksideeffects() {
var x int
f := func() int {
x++
return 0
}
m := make(map[int]int)
m[0] = 0
m[1] = 1
for k := range m {
delete(m, k+f())
}
if want := 2; x != want {
fmt.Printf("var after range with side-effect = %d want %d\n", x, want)
os.Exit(1)
}
var n int
m = make(map[int]int)
m[0] = 0
m[1] = 1
for k := range m {
delete(m, k)
n++
}
if want := 2; n != want {
fmt.Printf("counter for range with side-effect = %d want %d\n", n, want)
os.Exit(1)
}
}
func main() {
checkcleared()
checkloopvars()
checksideeffects()
}