Commit dad5d76e authored by Josh Bleecher Snyder's avatar Josh Bleecher Snyder

runtime: strength reduce key pointer calculations in mapaccess*_fast*

While we're here, check string length before checking b.tophash.

name                     old time/op  new time/op  delta
MapStringKeysEight_16-8  11.4ns ±10%   7.0ns ± 2%  -38.27%  (p=0.000 n=29+28)
MapStringKeysEight_32-8  10.9ns ± 2%   6.3ns ± 3%  -41.89%  (p=0.000 n=26+30)
MapStringKeysEight_64-8  10.8ns ± 3%   6.3ns ± 2%  -41.52%  (p=0.000 n=28+27)
MapStringKeysEight_1M-8  10.9ns ± 4%   6.3ns ± 2%  -41.91%  (p=0.000 n=29+29)
IntMap-8                 7.05ns ± 4%  6.77ns ± 3%   -3.94%  (p=0.000 n=29+30)


Change-Id: I0f3dc3301bdf550e4ac5250e1e64e7f2a0ffb269
Reviewed-on: https://go-review.googlesource.com/57590
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarKeith Randall <khr@golang.org>
parent 15d5fa02
...@@ -192,6 +192,10 @@ func (b *bmap) setoverflow(t *maptype, ovf *bmap) { ...@@ -192,6 +192,10 @@ func (b *bmap) setoverflow(t *maptype, ovf *bmap) {
*(**bmap)(add(unsafe.Pointer(b), uintptr(t.bucketsize)-sys.PtrSize)) = ovf *(**bmap)(add(unsafe.Pointer(b), uintptr(t.bucketsize)-sys.PtrSize)) = ovf
} }
func (b *bmap) keys() unsafe.Pointer {
return add(unsafe.Pointer(b), dataOffset)
}
// incrnoverflow increments h.noverflow. // incrnoverflow increments h.noverflow.
// noverflow counts the number of overflow buckets. // noverflow counts the number of overflow buckets.
// This is used to trigger same-size map growth. // This is used to trigger same-size map growth.
......
...@@ -40,15 +40,10 @@ func mapaccess1_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer { ...@@ -40,15 +40,10 @@ func mapaccess1_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer {
} }
} }
for { for {
for i := uintptr(0); i < bucketCnt; i++ { for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 4) {
k := *((*uint32)(add(unsafe.Pointer(b), dataOffset+i*4))) if *(*uint32)(k) == key && b.tophash[i] != empty {
if k != key { return add(unsafe.Pointer(b), dataOffset+bucketCnt*4+i*uintptr(t.valuesize))
continue
} }
if b.tophash[i] == empty {
continue
}
return add(unsafe.Pointer(b), dataOffset+bucketCnt*4+i*uintptr(t.valuesize))
} }
b = b.overflow(t) b = b.overflow(t)
if b == nil { if b == nil {
...@@ -88,15 +83,10 @@ func mapaccess2_fast32(t *maptype, h *hmap, key uint32) (unsafe.Pointer, bool) { ...@@ -88,15 +83,10 @@ func mapaccess2_fast32(t *maptype, h *hmap, key uint32) (unsafe.Pointer, bool) {
} }
} }
for { for {
for i := uintptr(0); i < bucketCnt; i++ { for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 4) {
k := *((*uint32)(add(unsafe.Pointer(b), dataOffset+i*4))) if *(*uint32)(k) == key && b.tophash[i] != empty {
if k != key { return add(unsafe.Pointer(b), dataOffset+bucketCnt*4+i*uintptr(t.valuesize)), true
continue
} }
if b.tophash[i] == empty {
continue
}
return add(unsafe.Pointer(b), dataOffset+bucketCnt*4+i*uintptr(t.valuesize)), true
} }
b = b.overflow(t) b = b.overflow(t)
if b == nil { if b == nil {
...@@ -136,15 +126,10 @@ func mapaccess1_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer { ...@@ -136,15 +126,10 @@ func mapaccess1_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer {
} }
} }
for { for {
for i := uintptr(0); i < bucketCnt; i++ { for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 8) {
k := *((*uint64)(add(unsafe.Pointer(b), dataOffset+i*8))) if *(*uint64)(k) == key && b.tophash[i] != empty {
if k != key { return add(unsafe.Pointer(b), dataOffset+bucketCnt*8+i*uintptr(t.valuesize))
continue
} }
if b.tophash[i] == empty {
continue
}
return add(unsafe.Pointer(b), dataOffset+bucketCnt*8+i*uintptr(t.valuesize))
} }
b = b.overflow(t) b = b.overflow(t)
if b == nil { if b == nil {
...@@ -184,15 +169,10 @@ func mapaccess2_fast64(t *maptype, h *hmap, key uint64) (unsafe.Pointer, bool) { ...@@ -184,15 +169,10 @@ func mapaccess2_fast64(t *maptype, h *hmap, key uint64) (unsafe.Pointer, bool) {
} }
} }
for { for {
for i := uintptr(0); i < bucketCnt; i++ { for i, k := uintptr(0), b.keys(); i < bucketCnt; i, k = i+1, add(k, 8) {
k := *((*uint64)(add(unsafe.Pointer(b), dataOffset+i*8))) if *(*uint64)(k) == key && b.tophash[i] != empty {
if k != key { return add(unsafe.Pointer(b), dataOffset+bucketCnt*8+i*uintptr(t.valuesize)), true
continue
} }
if b.tophash[i] == empty {
continue
}
return add(unsafe.Pointer(b), dataOffset+bucketCnt*8+i*uintptr(t.valuesize)), true
} }
b = b.overflow(t) b = b.overflow(t)
if b == nil { if b == nil {
...@@ -218,12 +198,9 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer { ...@@ -218,12 +198,9 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer {
b := (*bmap)(h.buckets) b := (*bmap)(h.buckets)
if key.len < 32 { if key.len < 32 {
// short key, doing lots of comparisons is ok // short key, doing lots of comparisons is ok
for i := uintptr(0); i < bucketCnt; i++ { for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) {
if b.tophash[i] == empty { k := (*stringStruct)(kptr)
continue if k.len != key.len || b.tophash[i] == empty {
}
k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+i*2*sys.PtrSize))
if k.len != key.len {
continue continue
} }
if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) {
...@@ -234,12 +211,9 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer { ...@@ -234,12 +211,9 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer {
} }
// long key, try not to do more comparisons than necessary // long key, try not to do more comparisons than necessary
keymaybe := uintptr(bucketCnt) keymaybe := uintptr(bucketCnt)
for i := uintptr(0); i < bucketCnt; i++ { for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) {
if b.tophash[i] == empty { k := (*stringStruct)(kptr)
continue if k.len != key.len || b.tophash[i] == empty {
}
k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+i*2*sys.PtrSize))
if k.len != key.len {
continue continue
} }
if k.str == key.str { if k.str == key.str {
...@@ -283,12 +257,9 @@ dohash: ...@@ -283,12 +257,9 @@ dohash:
} }
top := tophash(hash) top := tophash(hash)
for { for {
for i := uintptr(0); i < bucketCnt; i++ { for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) {
if b.tophash[i] != top { k := (*stringStruct)(kptr)
continue if k.len != key.len || b.tophash[i] != top {
}
k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+i*2*sys.PtrSize))
if k.len != key.len {
continue continue
} }
if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) {
...@@ -319,12 +290,9 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) { ...@@ -319,12 +290,9 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
b := (*bmap)(h.buckets) b := (*bmap)(h.buckets)
if key.len < 32 { if key.len < 32 {
// short key, doing lots of comparisons is ok // short key, doing lots of comparisons is ok
for i := uintptr(0); i < bucketCnt; i++ { for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) {
if b.tophash[i] == empty { k := (*stringStruct)(kptr)
continue if k.len != key.len || b.tophash[i] == empty {
}
k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+i*2*sys.PtrSize))
if k.len != key.len {
continue continue
} }
if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) {
...@@ -335,12 +303,9 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) { ...@@ -335,12 +303,9 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
} }
// long key, try not to do more comparisons than necessary // long key, try not to do more comparisons than necessary
keymaybe := uintptr(bucketCnt) keymaybe := uintptr(bucketCnt)
for i := uintptr(0); i < bucketCnt; i++ { for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) {
if b.tophash[i] == empty { k := (*stringStruct)(kptr)
continue if k.len != key.len || b.tophash[i] == empty {
}
k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+i*2*sys.PtrSize))
if k.len != key.len {
continue continue
} }
if k.str == key.str { if k.str == key.str {
...@@ -384,12 +349,9 @@ dohash: ...@@ -384,12 +349,9 @@ dohash:
} }
top := tophash(hash) top := tophash(hash)
for { for {
for i := uintptr(0); i < bucketCnt; i++ { for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) {
if b.tophash[i] != top { k := (*stringStruct)(kptr)
continue if k.len != key.len || b.tophash[i] != top {
}
k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+i*2*sys.PtrSize))
if k.len != key.len {
continue continue
} }
if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) {
......
...@@ -369,7 +369,7 @@ func TestIntendedInlining(t *testing.T) { ...@@ -369,7 +369,7 @@ func TestIntendedInlining(t *testing.T) {
t.Parallel() t.Parallel()
// want is the list of function names that should be inlined. // want is the list of function names that should be inlined.
want := []string{"tophash", "add"} want := []string{"tophash", "add", "(*bmap).keys"}
m := make(map[string]bool, len(want)) m := make(map[string]bool, len(want))
for _, s := range want { for _, s := range want {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment