aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorcuiweixie <cuiweixie@gmail.com>2023-04-03 20:14:06 +0800
committerGopher Robot <gobot@golang.org>2023-05-24 04:19:06 +0000
commitc6fd0c22dca065fbcf8f4a6516be34db408b4397 (patch)
treef1ac51f82f1c1ecbe99ce33a00729e73f6dee7bc /src
parent5b93af7f4f61ccc7d770ee24641b2fd9c71c0329 (diff)
downloadgo-c6fd0c22dca065fbcf8f4a6516be34db408b4397.tar.xz
maps,runtime: improve maps.Values
name old time/op new time/op delta Values-10 8.67ms ± 0% 7.19ms ± 2% -17.05% (p=0.000 n=9+10) name old alloc/op new alloc/op delta Values-10 58.2kB ± 2% 48.3kB ± 2% -17.14% (p=0.000 n=9+10) name old allocs/op new allocs/op delta Values-10 0.00 0.00 ~ (all equal) Change-Id: Idd35ea37514a21d97bdd6191c8fb8a478c00e414 Reviewed-on: https://go-review.googlesource.com/c/go/+/481436 Reviewed-by: Keith Randall <khr@golang.org> Run-TryBot: xie cui <523516579@qq.com> Reviewed-by: Keith Randall <khr@google.com> Reviewed-by: Ian Lance Taylor <iant@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Keith Randall <khr@golang.org>
Diffstat (limited to 'src')
-rw-r--r--src/maps/maps.go13
-rw-r--r--src/maps/maps_test.go27
-rw-r--r--src/runtime/map.go65
3 files changed, 102 insertions, 3 deletions
diff --git a/src/maps/maps.go b/src/maps/maps.go
index dddfb37973..15ec456a17 100644
--- a/src/maps/maps.go
+++ b/src/maps/maps.go
@@ -24,16 +24,23 @@ func keysForBenchmarking[M ~map[K]V, K comparable, V any](m M, s []K) {
keys(m, unsafe.Pointer(&s))
}
+// values is implemented in the runtime package.
+//
+//go:noescape
+func values(m any, slice unsafe.Pointer)
+
// 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 {
r := make([]V, 0, len(m))
- for _, v := range m {
- r = append(r, v)
- }
+ values(m, unsafe.Pointer(&r))
return r
}
+func valuesForBenchmarking[M ~map[K]V, K comparable, V any](m M, s []V) {
+ values(m, unsafe.Pointer(&s))
+}
+
// Equal reports whether two maps contain the same key/value pairs.
// Values are compared using ==.
func Equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool {
diff --git a/src/maps/maps_test.go b/src/maps/maps_test.go
index e7670839c9..6b92e0d8d6 100644
--- a/src/maps/maps_test.go
+++ b/src/maps/maps_test.go
@@ -62,6 +62,20 @@ func TestValues(t *testing.T) {
if !slices.Equal(got2, want2) {
t.Errorf("Values(%v) = %v, want %v", m2, got2, want2)
}
+
+ //test for oldbucket code path
+ var want3 []int
+ var m = make(map[int]int)
+ for i := 0; i < 840; i++ {
+ want3 = append(want3, i*i)
+ m[i] = i * i
+ }
+
+ got3 := Values(m)
+ sort.Ints(got3)
+ if !slices.Equal(got3, want3) {
+ t.Errorf("Values(%v) = %v, want %v", m, got3, want3)
+ }
}
func TestEqual(t *testing.T) {
@@ -246,3 +260,16 @@ func BenchmarkKeys(b *testing.B) {
keysForBenchmarking(m, slice)
}
}
+
+func BenchmarkValues(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++ {
+ valuesForBenchmarking(m, slice)
+ }
+}
diff --git a/src/runtime/map.go b/src/runtime/map.go
index 33685269cd..7b954759f1 100644
--- a/src/runtime/map.go
+++ b/src/runtime/map.go
@@ -1657,3 +1657,68 @@ func copyKeys(t *maptype, h *hmap, b *bmap, s *slice, offset uint8) {
b = b.overflow(t)
}
}
+
+// values for implementing maps.values
+//
+//go:linkname values maps.values
+func values(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 {
+ copyValues(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)))
+ copyValues(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
+ }
+ copyValues(t, h, b, s, offset)
+ }
+ }
+ return
+}
+
+func copyValues(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")
+ }
+
+ ele := add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.KeySize)+offi*uintptr(t.ValueSize))
+ if t.IndirectElem() {
+ ele = *((*unsafe.Pointer)(ele))
+ }
+ if s.len >= s.cap {
+ fatal("concurrent map read and map write")
+ }
+ typedmemmove(t.Elem, add(s.array, uintptr(s.len)*uintptr(t.ValueSize)), ele)
+ s.len++
+ }
+ b = b.overflow(t)
+ }
+}