name old time/op new time/op delta
Keys-10 8.65ms ± 0% 7.06ms ± 0% -18.41% (p=0.000 n=9+9)
name old alloc/op new alloc/op delta
Keys-10 58.2kB ± 1% 47.4kB ± 2% -18.54% (p=0.000 n=10+10)
name old allocs/op new allocs/op delta
Keys-10 0.00 0.00 ~ (all equal)
Change-Id: Ia7061c37be89e906e79bc3ef3bb1ef0d7913f9f6
Reviewed-on: https://go-review.googlesource.com/c/go/+/481435
Reviewed-by: Keith Randall <khr@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Run-TryBot: xie cui <
523516579@qq.com>
internal/goexperiment, internal/goos,
internal/goversion, internal/nettrace, internal/platform,
log/internal,
- maps, unicode/utf8, unicode/utf16, unicode,
+ unicode/utf8, unicode/utf16, unicode,
unsafe;
# slices depends on unsafe for overlapping check.
internal/goarch, unsafe
< internal/abi;
+ unsafe < maps;
+
# RUNTIME is the core runtime group of packages, all of them very light-weight.
internal/abi, internal/cpu, internal/goarch,
internal/coverage/rtcov, internal/godebugs, internal/goexperiment,
// Package maps defines various functions useful with maps of any type.
package maps
+import "unsafe"
+
+// keys is implemented in the runtime package.
+//
+//go:noescape
+func keys(m any, slice unsafe.Pointer)
+
// Keys returns the keys of the map m.
// The keys will be in an indeterminate order.
func Keys[M ~map[K]V, K comparable, V any](m M) []K {
r := make([]K, 0, len(m))
- for k := range m {
- r = append(r, k)
- }
+ keys(m, unsafe.Pointer(&r))
return r
}
+func keysForBenchmarking[M ~map[K]V, K comparable, V any](m M, s []K) {
+ keys(m, unsafe.Pointer(&s))
+}
+
// Values returns the values of the map m.
// The values will be in an indeterminate order.
func Values[M ~map[K]V, K comparable, V any](m M) []V {
if !slices.Equal(got2, want) {
t.Errorf("Keys(%v) = %v, want %v", m2, got2, want)
}
+
+ // test for oldbucket code path
+ // We grow from 128 to 256 buckets at size 832 (6.5 * 128).
+ // Then we have to evacuate 128 buckets, which means we'll be done evacuation at 832+128=960 elements inserted.
+ // so 840 is a good number to test for oldbucket code path.
+ var want3 []int
+ var m = make(map[int]int)
+ for i := 0; i < 840; i++ {
+ want3 = append(want3, i)
+ m[i] = i
+ }
+
+ got3 := Keys(m)
+ sort.Ints(got3)
+ if !slices.Equal(got3, want3) {
+ t.Errorf("Keys(%v) = %v, want %v", m, got3, want3)
+ }
}
func TestValues(t *testing.T) {
}
}
}
+
+var keysArr []int
+
+func BenchmarkKeys(b *testing.B) {
+ m := make(map[int]int, 1000000)
+ for i := 0; i < 1000000; i++ {
+ m[i] = i
+ }
+ b.ResetTimer()
+
+ slice := make([]int, 0, len(m))
+ for i := 0; i < b.N; i++ {
+ keysForBenchmarking(m, slice)
+ }
+}
}
return dst
}
+
+// keys for implementing maps.keys
+//
+//go:linkname keys maps.keys
+func keys(m any, p unsafe.Pointer) {
+ e := efaceOf(&m)
+ t := (*maptype)(unsafe.Pointer(e._type))
+ h := (*hmap)(e.data)
+
+ if h == nil || h.count == 0 {
+ return
+ }
+ s := (*slice)(p)
+ r := int(fastrand())
+ offset := uint8(r >> h.B & (bucketCnt - 1))
+ if h.B == 0 {
+ copyKeys(t, h, (*bmap)(h.buckets), s, offset)
+ return
+ }
+ arraySize := int(bucketShift(h.B))
+ buckets := h.buckets
+ for i := 0; i < arraySize; i++ {
+ bucket := (i + r) & (arraySize - 1)
+ b := (*bmap)(add(buckets, uintptr(bucket)*uintptr(t.BucketSize)))
+ copyKeys(t, h, b, s, offset)
+ }
+
+ if h.growing() {
+ oldArraySize := int(h.noldbuckets())
+ for i := 0; i < oldArraySize; i++ {
+ bucket := (i + r) & (oldArraySize - 1)
+ b := (*bmap)(add(h.oldbuckets, uintptr(bucket)*uintptr(t.BucketSize)))
+ if evacuated(b) {
+ continue
+ }
+ copyKeys(t, h, b, s, offset)
+ }
+ }
+ return
+}
+
+func copyKeys(t *maptype, h *hmap, b *bmap, s *slice, offset uint8) {
+ for b != nil {
+ for i := uintptr(0); i < bucketCnt; i++ {
+ offi := (i + uintptr(offset)) & (bucketCnt - 1)
+ if isEmpty(b.tophash[offi]) {
+ continue
+ }
+ if h.flags&hashWriting != 0 {
+ fatal("concurrent map read and map write")
+ }
+ k := add(unsafe.Pointer(b), dataOffset+offi*uintptr(t.KeySize))
+ if t.IndirectKey() {
+ k = *((*unsafe.Pointer)(k))
+ }
+ if s.len >= s.cap {
+ fatal("concurrent map read and map write")
+ }
+ typedmemmove(t.Key, add(s.array, uintptr(s.len)*uintptr(t.KeySize)), k)
+ s.len++
+ }
+ b = b.overflow(t)
+ }
+}