mirror of
https://github.com/golang/go.git
synced 2026-06-27 19:30:52 +00:00
internal/runtime/maps,runtime/: pass keys by value to MemHash{32,64} and StrHash.
This removes redundant loads and stores when hashing keys in specialized maps and avoids treating keys as address-taken, which previously caused unnecessary reloads inside the loop. Also remove the separate strHashAES implementation and route string hashing through memHashAES directly, simplifying the AES hashing code paths. Change-Id: I498ae9f437ad089df4298aa85f04a16c2ed6a5f5 Reviewed-on: https://go-review.googlesource.com/c/go/+/776281 Reviewed-by: Keith Randall <khr@golang.org> Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@google.com> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Cherry Mui <cherryyz@google.com>
This commit is contained in:
parent
e26a373785
commit
05f75fb9e8
16 changed files with 137 additions and 181 deletions
|
|
@ -5,43 +5,36 @@
|
|||
#include "textflag.h"
|
||||
|
||||
// hash function using AES hardware instructions
|
||||
|
||||
// func memHash32AES(k uint32, h uintptr) uintptr
|
||||
TEXT ·memHash32AES(SB),NOSPLIT,$0-12
|
||||
MOVL p+0(FP), AX // ptr to data
|
||||
MOVL h+4(FP), X0 // seed
|
||||
PINSRD $1, (AX), X0 // data
|
||||
MOVL h+4(FP), X0 // seed
|
||||
PINSRD $1, k+0(FP), X0 // data
|
||||
AESENC ·aeskeysched+0(SB), X0
|
||||
AESENC ·aeskeysched+16(SB), X0
|
||||
AESENC ·aeskeysched+32(SB), X0
|
||||
MOVL X0, ret+8(FP)
|
||||
RET
|
||||
|
||||
TEXT ·memHash64AES(SB),NOSPLIT,$0-12
|
||||
MOVL p+0(FP), AX // ptr to data
|
||||
MOVQ (AX), X0 // data
|
||||
PINSRD $2, h+4(FP), X0 // seed
|
||||
// func memHash64AES(k uint64, h uintptr) uintptr
|
||||
TEXT ·memHash64AES(SB),NOSPLIT,$0-16
|
||||
MOVQ k+0(FP), X0 // data
|
||||
PINSRD $2, h+8(FP), X0 // seed
|
||||
AESENC ·aeskeysched+0(SB), X0
|
||||
AESENC ·aeskeysched+16(SB), X0
|
||||
AESENC ·aeskeysched+32(SB), X0
|
||||
MOVL X0, ret+8(FP)
|
||||
MOVL X0, ret+12(FP)
|
||||
RET
|
||||
|
||||
// func memHashAES(p unsafe.Pointer, h, size uintptr) uintptr
|
||||
TEXT ·memHashAES(SB),NOSPLIT,$0-16
|
||||
MOVL p+0(FP), AX // ptr to data
|
||||
MOVL s+8(FP), BX // size
|
||||
// AX: data
|
||||
// BX: size
|
||||
// DX: address to put return value
|
||||
MOVL p+0(FP), AX
|
||||
MOVL s+8(FP), BX
|
||||
LEAL ret+12(FP), DX
|
||||
JMP ·aeshashbody<>(SB)
|
||||
|
||||
TEXT ·strHashAES(SB),NOSPLIT,$0-12
|
||||
MOVL p+0(FP), AX // ptr to string object
|
||||
MOVL 4(AX), BX // length of string
|
||||
MOVL (AX), AX // string data
|
||||
LEAL ret+8(FP), DX
|
||||
JMP ·aeshashbody<>(SB)
|
||||
|
||||
// AX: data
|
||||
// BX: length
|
||||
// DX: address to put return value
|
||||
TEXT ·aeshashbody<>(SB),NOSPLIT,$0-0
|
||||
MOVL h+4(FP), X0 // 32 bits of per-table hash seed
|
||||
PINSRW $4, BX, X0 // 16 bits of length
|
||||
PSHUFHW $0, X0, X0 // replace size with its low 2 bytes repeated 4 times
|
||||
|
|
|
|||
|
|
@ -19,23 +19,20 @@ func MemHash(p unsafe.Pointer, h, s uintptr) uintptr {
|
|||
return memHashFallback(p, h, s)
|
||||
}
|
||||
|
||||
func MemHash32(p unsafe.Pointer, h uintptr) uintptr {
|
||||
func MemHash32(k uint32, h uintptr) uintptr {
|
||||
if UseAeshash {
|
||||
return memHash32AES(p, h)
|
||||
return memHash32AES(k, h)
|
||||
}
|
||||
return memHash32Fallback(p, h)
|
||||
return memHash32Fallback(k, h)
|
||||
}
|
||||
|
||||
func MemHash64(p unsafe.Pointer, h uintptr) uintptr {
|
||||
func MemHash64(k uint64, h uintptr) uintptr {
|
||||
if UseAeshash {
|
||||
return memHash64AES(p, h)
|
||||
return memHash64AES(k, h)
|
||||
}
|
||||
return memHash64Fallback(p, h)
|
||||
return memHash64Fallback(k, h)
|
||||
}
|
||||
|
||||
func StrHash(p unsafe.Pointer, h uintptr) uintptr {
|
||||
if UseAeshash {
|
||||
return strHashAES(p, h)
|
||||
}
|
||||
return strHashFallback(p, h)
|
||||
func StrHash(s string, h uintptr) uintptr {
|
||||
return MemHash(unsafe.Pointer(unsafe.StringData(s)), h, uintptr(len(s)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,7 @@ const memHashUsesVAES = false
|
|||
func memHashAES(p unsafe.Pointer, h, s uintptr) uintptr
|
||||
|
||||
//go:noescape
|
||||
func memHash32AES(p unsafe.Pointer, h uintptr) uintptr
|
||||
func memHash32AES(k uint32, h uintptr) uintptr
|
||||
|
||||
//go:noescape
|
||||
func memHash64AES(p unsafe.Pointer, h uintptr) uintptr
|
||||
|
||||
//go:noescape
|
||||
func strHashAES(p unsafe.Pointer, h uintptr) uintptr
|
||||
func memHash64AES(k uint64, h uintptr) uintptr
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ import (
|
|||
|
||||
const memHashUsesVAES = true
|
||||
|
||||
func memHash32AES(p unsafe.Pointer, seed uintptr) uintptr {
|
||||
func memHash32AES(k uint32, seed uintptr) uintptr {
|
||||
var state archsimd.Uint64x2
|
||||
state = state.SetElem(0, uint64(seed)).SetElem(1, uint64(*(*uint32)(p)))
|
||||
state = state.SetElem(0, uint64(seed)).SetElem(1, uint64(k))
|
||||
|
||||
hash := state.
|
||||
AsUint8x16().
|
||||
|
|
@ -27,9 +27,9 @@ func memHash32AES(p unsafe.Pointer, seed uintptr) uintptr {
|
|||
return uintptr(hash)
|
||||
}
|
||||
|
||||
func memHash64AES(p unsafe.Pointer, seed uintptr) uintptr {
|
||||
func memHash64AES(k uint64, seed uintptr) uintptr {
|
||||
var state archsimd.Uint64x2
|
||||
state = state.SetElem(0, uint64(seed)).SetElem(1, *(*uint64)(p))
|
||||
state = state.SetElem(0, uint64(seed)).SetElem(1, k)
|
||||
|
||||
hash := state.
|
||||
AsUint8x16().
|
||||
|
|
@ -41,12 +41,9 @@ func memHash64AES(p unsafe.Pointer, seed uintptr) uintptr {
|
|||
return uintptr(hash)
|
||||
}
|
||||
|
||||
// TODO: Both strHashAES and memHashAES use aeshashbody that is quite large.
|
||||
// So there is no point in rewriting them using simd intrinsics, since they won't be inlinable.
|
||||
// TODO: memHashAES is quite large.
|
||||
// So there is no point in rewriting it using simd intrinsics, since it won't be inlinable.
|
||||
// Maybe in future we can do it for better maitanability.
|
||||
//
|
||||
//go:noescape
|
||||
func memHashAES(p unsafe.Pointer, h, s uintptr) uintptr
|
||||
|
||||
//go:noescape
|
||||
func strHashAES(p unsafe.Pointer, h uintptr) uintptr
|
||||
|
|
|
|||
|
|
@ -7,24 +7,11 @@
|
|||
// func memHashAES(p unsafe.Pointer, h, s uintptr) uintptr
|
||||
// hash function using AES hardware instructions
|
||||
TEXT ·memHashAES<ABIInternal>(SB),NOSPLIT,$0-32
|
||||
// AX = ptr to data
|
||||
// BX = seed
|
||||
// CX = size
|
||||
JMP ·aeshashbody<>(SB)
|
||||
// AX: data
|
||||
// BX: hash seed
|
||||
// CX: length
|
||||
// At return: AX = return value
|
||||
|
||||
// func strhashAES(p unsafe.Pointer, h uintptr) uintptr
|
||||
TEXT ·strHashAES<ABIInternal>(SB),NOSPLIT,$0-24
|
||||
// AX = ptr to string struct
|
||||
// BX = seed
|
||||
MOVQ 8(AX), CX // length of string
|
||||
MOVQ (AX), AX // string data
|
||||
JMP ·aeshashbody<>(SB)
|
||||
|
||||
// AX: data
|
||||
// BX: hash seed
|
||||
// CX: length
|
||||
// At return: AX = return value
|
||||
TEXT ·aeshashbody<>(SB),NOSPLIT,$0-0
|
||||
// Fill an SSE register with our seeds.
|
||||
MOVQ BX, X0 // 64 bits of per-table hash seed
|
||||
PINSRW $4, CX, X0 // 16 bits of length
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@
|
|||
|
||||
#include "textflag.h"
|
||||
|
||||
// func memHash32AES(p unsafe.Pointer, h uintptr) uintptr
|
||||
// func memHash32AES(k uint32, h uintptr) uintptr
|
||||
TEXT ·memHash32AES<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24
|
||||
MOVD $·aeskeysched+0(SB), R3
|
||||
|
||||
VEOR V0.B16, V0.B16, V0.B16
|
||||
VLD1 (R3), [V2.B16]
|
||||
VLD1 (R0), V0.S[2]
|
||||
VMOV R0, V0.S[2]
|
||||
VMOV R1, V0.D[0]
|
||||
|
||||
AESE V2.B16, V0.B16
|
||||
|
|
@ -22,13 +22,13 @@ TEXT ·memHash32AES<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24
|
|||
VMOV V0.D[0], R0
|
||||
RET
|
||||
|
||||
// func memHash64AES(p unsafe.Pointer, h uintptr) uintptr
|
||||
// func memHash64AES(k uint64, h uintptr) uintptr
|
||||
TEXT ·memHash64AES<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24
|
||||
MOVD $·aeskeysched+0(SB), R3
|
||||
|
||||
VEOR V0.B16, V0.B16, V0.B16
|
||||
VLD1 (R3), [V2.B16]
|
||||
VLD1 (R0), V0.D[1]
|
||||
VMOV R0, V0.D[1]
|
||||
VMOV R1, V0.D[0]
|
||||
|
||||
AESE V2.B16, V0.B16
|
||||
|
|
@ -42,18 +42,10 @@ TEXT ·memHash64AES<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24
|
|||
|
||||
// func memHashAES(p unsafe.Pointer, h, size uintptr) uintptr
|
||||
TEXT ·memHashAES<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-32
|
||||
B ·aeshashbody<>(SB)
|
||||
|
||||
// func strHashAES(p unsafe.Pointer, h uintptr) uintptr
|
||||
TEXT ·strHashAES<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24
|
||||
LDP (R0), (R0, R2) // string data / length
|
||||
B ·aeshashbody<>(SB)
|
||||
|
||||
// R0: data
|
||||
// R1: seed data
|
||||
// R2: length
|
||||
// At return, R0 = return value
|
||||
TEXT ·aeshashbody<>(SB),NOSPLIT|NOFRAME,$0
|
||||
// R0: data
|
||||
// R1: seed data
|
||||
// R2: length
|
||||
// At return, R0 = return value
|
||||
VEOR V30.B16, V30.B16, V30.B16
|
||||
VMOV R1, V30.D[0]
|
||||
VMOV R2, V30.D[1] // load length into seed
|
||||
|
|
|
|||
|
|
@ -14,30 +14,30 @@ import (
|
|||
const memHashAESImplemented = false
|
||||
const memHashUsesVAES = false
|
||||
|
||||
func memHash32AES(p unsafe.Pointer, h uintptr) uintptr {
|
||||
func memHash32AES(k uint32, h uintptr) uintptr {
|
||||
panic("memHash32AES not implemented")
|
||||
}
|
||||
|
||||
func memHash64AES(p unsafe.Pointer, h uintptr) uintptr {
|
||||
func memHash64AES(k uint64, h uintptr) uintptr {
|
||||
panic("memHash64AES not implemented")
|
||||
}
|
||||
|
||||
func strHashAES(p unsafe.Pointer, h uintptr) uintptr {
|
||||
panic("strHashAES not implemented")
|
||||
func memHashAES(p unsafe.Pointer, h, s uintptr) uintptr {
|
||||
panic("memHashAES not implemented")
|
||||
}
|
||||
|
||||
func MemHash(p unsafe.Pointer, h, s uintptr) uintptr {
|
||||
return memHashFallback(p, h, s)
|
||||
}
|
||||
|
||||
func MemHash32(p unsafe.Pointer, h uintptr) uintptr {
|
||||
return memHash32Fallback(p, h)
|
||||
func MemHash32(k uint32, h uintptr) uintptr {
|
||||
return memHash32Fallback(k, h)
|
||||
}
|
||||
|
||||
func MemHash64(p unsafe.Pointer, h uintptr) uintptr {
|
||||
return memHash64Fallback(p, h)
|
||||
func MemHash64(k uint64, h uintptr) uintptr {
|
||||
return memHash64Fallback(k, h)
|
||||
}
|
||||
|
||||
func StrHash(p unsafe.Pointer, h uintptr) uintptr {
|
||||
return strHashFallback(p, h)
|
||||
func StrHash(s string, h uintptr) uintptr {
|
||||
return memHashFallback(unsafe.Pointer(unsafe.StringData(s)), h, uintptr(len(s)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,26 +6,26 @@
|
|||
|
||||
#include "textflag.h"
|
||||
|
||||
// func memHash32AES(p unsafe.Pointer, h uintptr) uintptr
|
||||
// func memHash32AES(k uint32, h uintptr) uintptr
|
||||
// ABIInternal for performance.
|
||||
TEXT ·memHash32AES<ABIInternal>(SB),NOSPLIT,$0-24
|
||||
// AX = ptr to data
|
||||
// BX = seed
|
||||
MOVQ BX, X0 // X0 = seed
|
||||
PINSRD $2, (AX), X0 // data
|
||||
MOVQ BX, X0 // X0 = seed
|
||||
PINSRD $2, AX, X0 // data
|
||||
AESENC ·aeskeysched+0(SB), X0
|
||||
AESENC ·aeskeysched+16(SB), X0
|
||||
AESENC ·aeskeysched+32(SB), X0
|
||||
MOVQ X0, AX // return X0
|
||||
RET
|
||||
|
||||
// func memHash64AES(p unsafe.Pointer, h uintptr) uintptr
|
||||
// func memHash64AES(k uint64, h uintptr) uintptr
|
||||
// ABIInternal for performance.
|
||||
TEXT ·memHash64AES<ABIInternal>(SB),NOSPLIT,$0-24
|
||||
// AX = ptr to data
|
||||
// BX = seed
|
||||
MOVQ BX, X0 // X0 = seed
|
||||
PINSRQ $1, (AX), X0 // data
|
||||
MOVQ BX, X0 // X0 = seed
|
||||
PINSRQ $1, AX, X0 // data
|
||||
AESENC ·aeskeysched+0(SB), X0
|
||||
AESENC ·aeskeysched+16(SB), X0
|
||||
AESENC ·aeskeysched+32(SB), X0
|
||||
|
|
|
|||
|
|
@ -38,8 +38,7 @@ func AlgInit() {
|
|||
cpu.X86.HasSSSE3 && // PSHUFB
|
||||
cpu.X86.HasSSE41 { // PINSR{D,Q}
|
||||
|
||||
// In aeshashbody (that is used by memhash & strhash)
|
||||
// we have global variables that should be properly aligned.
|
||||
// In memHashAES we have global variables that should be properly aligned.
|
||||
//
|
||||
// See #12415
|
||||
if !checkMasksAndShiftsAlignment() {
|
||||
|
|
@ -71,15 +70,6 @@ func initAlgAES() {
|
|||
}
|
||||
}
|
||||
|
||||
func strHashFallback(a unsafe.Pointer, h uintptr) uintptr {
|
||||
type stringStruct struct {
|
||||
str unsafe.Pointer
|
||||
len int
|
||||
}
|
||||
x := (*stringStruct)(a)
|
||||
return memHashFallback(x.str, h, uintptr(x.len))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
|
||||
return unsafe.Pointer(uintptr(p) + x)
|
||||
|
|
|
|||
|
|
@ -63,18 +63,6 @@ func runtime_mapaccess2_fast32(typ *abi.MapType, m *Map, key uint32) (unsafe.Poi
|
|||
return unsafe.Pointer(&zeroVal[0]), false
|
||||
}
|
||||
|
||||
// Don't pass address of the key directly to the hashing function.
|
||||
// Hashing functions are implemented in Go assembly and cannot be inlined,
|
||||
// so compiler doesn't optimize redundant address taking/dereference.
|
||||
//
|
||||
// Taking &key makes compiler treat key as address-taken, which forces it to spill on the stack
|
||||
// and reload it in the loop.
|
||||
// This is suboptimal for performance.
|
||||
//
|
||||
// Note: Even when we pass k (local copy of key), the compiler still spills the key to the stack.
|
||||
// However, from compiler's perspective, key is no longer address-taken and
|
||||
// filled back in register before the loop.
|
||||
k := key
|
||||
var hash uintptr
|
||||
// Explicitly inline MemHash32.
|
||||
// MemHash32 cost is higher than the threshold for inlining.
|
||||
|
|
@ -84,9 +72,9 @@ func runtime_mapaccess2_fast32(typ *abi.MapType, m *Map, key uint32) (unsafe.Poi
|
|||
// Note: memHashAESImplemented is compile time constant. We use it to remove runtime UseAeshash check
|
||||
// for architectures where we don't have AES hashing implementations.
|
||||
if memHashAESImplemented && UseAeshash {
|
||||
hash = memHash32AES(unsafe.Pointer(&k), m.seed)
|
||||
hash = memHash32AES(key, m.seed)
|
||||
} else {
|
||||
hash = memHash32Fallback(unsafe.Pointer(&k), m.seed)
|
||||
hash = memHash32Fallback(key, m.seed)
|
||||
}
|
||||
|
||||
// Select table.
|
||||
|
|
@ -209,15 +197,12 @@ func runtime_mapassign_fast32(typ *abi.MapType, m *Map, key uint32) unsafe.Point
|
|||
fatal("concurrent map writes")
|
||||
}
|
||||
|
||||
// See the related comment in runtime_mapaccess2_fast32
|
||||
// for why we pass local copy of key.
|
||||
k := key
|
||||
var hash uintptr
|
||||
// See the related comment in runtime_mapaccess2_fast32
|
||||
if memHashAESImplemented && UseAeshash {
|
||||
hash = memHash32AES(unsafe.Pointer(&k), m.seed)
|
||||
hash = memHash32AES(key, m.seed)
|
||||
} else {
|
||||
hash = memHash32Fallback(unsafe.Pointer(&k), m.seed)
|
||||
hash = memHash32Fallback(key, m.seed)
|
||||
}
|
||||
|
||||
// Set writing after calling Hasher, since Hasher may panic, in which
|
||||
|
|
@ -361,15 +346,12 @@ func runtime_mapassign_fast32ptr(typ *abi.MapType, m *Map, key unsafe.Pointer) u
|
|||
fatal("concurrent map writes")
|
||||
}
|
||||
|
||||
// See the related comment in runtime_mapaccess2_fast32
|
||||
// for why we pass local copy of key.
|
||||
k := key
|
||||
var hash uintptr
|
||||
// See the related comment in runtime_mapaccess2_fast32
|
||||
if memHashAESImplemented && UseAeshash {
|
||||
hash = memHash32AES(unsafe.Pointer(&k), m.seed)
|
||||
hash = memHash32AES(uint32((uintptr)(key)), m.seed)
|
||||
} else {
|
||||
hash = memHash32Fallback(unsafe.Pointer(&k), m.seed)
|
||||
hash = memHash32Fallback(uint32((uintptr)(key)), m.seed)
|
||||
}
|
||||
|
||||
// Set writing after calling Hasher, since Hasher may panic, in which
|
||||
|
|
|
|||
|
|
@ -63,15 +63,12 @@ func runtime_mapaccess2_fast64(typ *abi.MapType, m *Map, key uint64) (unsafe.Poi
|
|||
return unsafe.Pointer(&zeroVal[0]), false
|
||||
}
|
||||
|
||||
// See the related comment in runtime_mapaccess2_fast32
|
||||
// for why we pass local copy of key.
|
||||
k := key
|
||||
var hash uintptr
|
||||
// See the related comment in runtime_mapaccess2_fast32
|
||||
if memHashAESImplemented && UseAeshash {
|
||||
hash = memHash64AES(unsafe.Pointer(&k), m.seed)
|
||||
hash = memHash64AES(key, m.seed)
|
||||
} else {
|
||||
hash = memHash64Fallback(unsafe.Pointer(&k), m.seed)
|
||||
hash = memHash64Fallback(key, m.seed)
|
||||
}
|
||||
|
||||
// Select table.
|
||||
|
|
@ -195,15 +192,12 @@ func runtime_mapassign_fast64(typ *abi.MapType, m *Map, key uint64) unsafe.Point
|
|||
fatal("concurrent map writes")
|
||||
}
|
||||
|
||||
// See the related comment in runtime_mapaccess2_fast32
|
||||
// for why we pass local copy of key.
|
||||
k := key
|
||||
var hash uintptr
|
||||
// See the related comment in runtime_mapaccess2_fast32
|
||||
if memHashAESImplemented && UseAeshash {
|
||||
hash = memHash64AES(unsafe.Pointer(&k), m.seed)
|
||||
hash = memHash64AES(key, m.seed)
|
||||
} else {
|
||||
hash = memHash64Fallback(unsafe.Pointer(&k), m.seed)
|
||||
hash = memHash64Fallback(key, m.seed)
|
||||
}
|
||||
|
||||
// Set writing after calling Hasher, since Hasher may panic, in which
|
||||
|
|
@ -416,15 +410,12 @@ func runtime_mapassign_fast64ptr(typ *abi.MapType, m *Map, key unsafe.Pointer) u
|
|||
fatal("concurrent map writes")
|
||||
}
|
||||
|
||||
// See the related comment in runtime_mapaccess2_fast32
|
||||
// for why we pass local copy of key.
|
||||
k := key
|
||||
var hash uintptr
|
||||
// See the related comment in runtime_mapaccess2_fast32
|
||||
if memHashAESImplemented && UseAeshash {
|
||||
hash = memHash64AES(unsafe.Pointer(&k), m.seed)
|
||||
hash = memHash64AES(uint64((uintptr)(key)), m.seed)
|
||||
} else {
|
||||
hash = memHash64Fallback(unsafe.Pointer(&k), m.seed)
|
||||
hash = memHash64Fallback(uint64((uintptr)(key)), m.seed)
|
||||
}
|
||||
|
||||
// Set writing after calling Hasher, since Hasher may panic, in which
|
||||
|
|
|
|||
|
|
@ -64,11 +64,13 @@ func (m *Map) getWithoutKeySmallFastStr(typ *abi.MapType, key string) unsafe.Poi
|
|||
|
||||
dohash:
|
||||
// This path will cost 1 hash and 1+ε comparisons.
|
||||
|
||||
var hash uintptr
|
||||
// See the related comment in runtime_mapaccess2_fast32
|
||||
// for why we pass local copy of key.
|
||||
k := key
|
||||
hash := StrHash(unsafe.Pointer(&k), m.seed)
|
||||
if memHashAESImplemented && UseAeshash {
|
||||
hash = memHashAES(unsafe.Pointer(unsafe.StringData(key)), m.seed, uintptr(len(key)))
|
||||
} else {
|
||||
hash = memHashFallback(unsafe.Pointer(unsafe.StringData(key)), m.seed, uintptr(len(key)))
|
||||
}
|
||||
h2 := uint8(h2(hash))
|
||||
ctrls = *g.ctrls()
|
||||
slotKey = g.key(typ, 0)
|
||||
|
|
@ -94,26 +96,19 @@ func longStringQuickEqualityTest(a, b string) bool {
|
|||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
x, y := stringPtr(a), stringPtr(b)
|
||||
x, y := unsafe.Pointer(unsafe.StringData(a)), unsafe.Pointer(unsafe.StringData(b))
|
||||
// Check first 8 bytes.
|
||||
if *(*[8]byte)(x) != *(*[8]byte)(y) {
|
||||
return false
|
||||
}
|
||||
// Check last 8 bytes.
|
||||
x = unsafe.Pointer(uintptr(x) + uintptr(len(a)) - 8)
|
||||
y = unsafe.Pointer(uintptr(y) + uintptr(len(a)) - 8)
|
||||
x = add(x, uintptr(len(a)-8))
|
||||
y = add(y, uintptr(len(a)-8))
|
||||
if *(*[8]byte)(x) != *(*[8]byte)(y) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
func stringPtr(s string) unsafe.Pointer {
|
||||
type stringStruct struct {
|
||||
ptr unsafe.Pointer
|
||||
len int
|
||||
}
|
||||
return (*stringStruct)(unsafe.Pointer(&s)).ptr
|
||||
}
|
||||
|
||||
//go:linkname runtime_mapaccess1_faststr runtime.mapaccess1_faststr
|
||||
func runtime_mapaccess1_faststr(typ *abi.MapType, m *Map, key string) unsafe.Pointer {
|
||||
|
|
@ -146,10 +141,13 @@ func runtime_mapaccess2_faststr(typ *abi.MapType, m *Map, key string) (unsafe.Po
|
|||
return elem, true
|
||||
}
|
||||
|
||||
var hash uintptr
|
||||
// See the related comment in runtime_mapaccess2_fast32
|
||||
// for why we pass local copy of key.
|
||||
k := key
|
||||
hash := StrHash(unsafe.Pointer(&k), m.seed)
|
||||
if memHashAESImplemented && UseAeshash {
|
||||
hash = memHashAES(unsafe.Pointer(unsafe.StringData(key)), m.seed, uintptr(len(key)))
|
||||
} else {
|
||||
hash = memHashFallback(unsafe.Pointer(unsafe.StringData(key)), m.seed, uintptr(len(key)))
|
||||
}
|
||||
|
||||
// Select table.
|
||||
idx := m.directoryIndex(hash)
|
||||
|
|
@ -273,10 +271,13 @@ func runtime_mapassign_faststr(typ *abi.MapType, m *Map, key string) unsafe.Poin
|
|||
fatal("concurrent map writes")
|
||||
}
|
||||
|
||||
var hash uintptr
|
||||
// See the related comment in runtime_mapaccess2_fast32
|
||||
// for why we pass local copy of key.
|
||||
k := key
|
||||
hash := StrHash(unsafe.Pointer(&k), m.seed)
|
||||
if memHashAESImplemented && UseAeshash {
|
||||
hash = memHashAES(unsafe.Pointer(unsafe.StringData(key)), m.seed, uintptr(len(key)))
|
||||
} else {
|
||||
hash = memHashFallback(unsafe.Pointer(unsafe.StringData(key)), m.seed, uintptr(len(key)))
|
||||
}
|
||||
|
||||
// Set writing after calling Hasher, since Hasher may panic, in which
|
||||
// case we have not actually done a write.
|
||||
|
|
|
|||
|
|
@ -11,27 +11,25 @@ package maps
|
|||
|
||||
import "unsafe"
|
||||
|
||||
func memHash32Fallback(p unsafe.Pointer, seed uintptr) uintptr {
|
||||
func memHash32Fallback(k uint32, seed uintptr) uintptr {
|
||||
a, b := mix32(uint32(seed), uint32(4^hashkey[0]))
|
||||
t := readUnaligned32(p)
|
||||
a ^= t
|
||||
b ^= t
|
||||
a ^= k
|
||||
b ^= k
|
||||
a, b = mix32(a, b)
|
||||
a, b = mix32(a, b)
|
||||
return uintptr(a ^ b)
|
||||
}
|
||||
|
||||
func memHash64Fallback(p unsafe.Pointer, seed uintptr) uintptr {
|
||||
func memHash64Fallback(k uint64, seed uintptr) uintptr {
|
||||
a, b := mix32(uint32(seed), uint32(8^hashkey[0]))
|
||||
a ^= readUnaligned32(p)
|
||||
b ^= readUnaligned32(add(p, 4))
|
||||
a ^= uint32(k)
|
||||
b ^= uint32(k >> 32)
|
||||
a, b = mix32(a, b)
|
||||
a, b = mix32(a, b)
|
||||
return uintptr(a ^ b)
|
||||
}
|
||||
|
||||
func memHashFallback(p unsafe.Pointer, seed, s uintptr) uintptr {
|
||||
|
||||
a, b := mix32(uint32(seed), uint32(s^hashkey[0]))
|
||||
if s == 0 {
|
||||
return uintptr(a ^ b)
|
||||
|
|
|
|||
|
|
@ -64,13 +64,13 @@ func memHashFallback(p unsafe.Pointer, seed, s uintptr) uintptr {
|
|||
return mix(m5^s, mix(a^hashkey[1], b^seed))
|
||||
}
|
||||
|
||||
func memHash32Fallback(p unsafe.Pointer, seed uintptr) uintptr {
|
||||
a := r4(p)
|
||||
func memHash32Fallback(k uint32, seed uintptr) uintptr {
|
||||
a := uintptr(k)
|
||||
return mix(m5^4, mix(a^hashkey[1], a^seed^hashkey[0]))
|
||||
}
|
||||
|
||||
func memHash64Fallback(p unsafe.Pointer, seed uintptr) uintptr {
|
||||
a := r8(p)
|
||||
func memHash64Fallback(k uint64, seed uintptr) uintptr {
|
||||
a := uintptr(k)
|
||||
return mix(m5^8, mix(a^hashkey[1], a^seed^hashkey[0]))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,12 +84,12 @@ func memhash(p unsafe.Pointer, h, s uintptr) uintptr {
|
|||
|
||||
//go:nosplit
|
||||
func memhash64(p unsafe.Pointer, seed uintptr) uintptr {
|
||||
return maps.MemHash64(p, seed)
|
||||
return maps.MemHash64(readUnaligned64(p), seed)
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func memhash32(p unsafe.Pointer, seed uintptr) uintptr {
|
||||
return maps.MemHash32(p, seed)
|
||||
return maps.MemHash32(readUnaligned32(p), seed)
|
||||
}
|
||||
|
||||
// strhash should be an internal detail,
|
||||
|
|
@ -104,9 +104,10 @@ func memhash32(p unsafe.Pointer, seed uintptr) uintptr {
|
|||
// Do not remove or change the type signature.
|
||||
// See go.dev/issue/67401.
|
||||
//
|
||||
//go:nosplit
|
||||
//go:linkname strhash
|
||||
func strhash(p unsafe.Pointer, h uintptr) uintptr {
|
||||
return maps.StrHash(p, h)
|
||||
return maps.StrHash(*(*string)(p), h)
|
||||
}
|
||||
|
||||
// NOTE: Because NaN != NaN, a map can contain any
|
||||
|
|
@ -384,6 +385,14 @@ func ifaceHash(i interface {
|
|||
return interhash(noescape(unsafe.Pointer(&i)), seed)
|
||||
}
|
||||
|
||||
func readUnaligned32(p unsafe.Pointer) uint32 {
|
||||
q := (*[4]byte)(p)
|
||||
if goarch.BigEndian {
|
||||
return byteorder.BEUint32(q[:])
|
||||
}
|
||||
return byteorder.LEUint32(q[:])
|
||||
}
|
||||
|
||||
func readUnaligned64(p unsafe.Pointer) uint64 {
|
||||
q := (*[8]byte)(p)
|
||||
if goarch.BigEndian {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,17 @@ import (
|
|||
"unsafe"
|
||||
)
|
||||
|
||||
// Test that unalgined access to memhash32 doesn't cause a problem.
|
||||
func TestMemHash32AlignAccess(t *testing.T) {
|
||||
type Key struct {
|
||||
_ [1]byte
|
||||
k [4]byte
|
||||
_ [3]byte
|
||||
}
|
||||
key := Key{}
|
||||
sink = (uint64)(MemHash32(unsafe.Pointer(&key.k), 0))
|
||||
}
|
||||
|
||||
func TestMemHash32Equality(t *testing.T) {
|
||||
if *UseAeshash {
|
||||
t.Skip("skipping since AES hash implementation is used")
|
||||
|
|
@ -37,6 +48,17 @@ func TestMemHash32Equality(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Test that unalgined access to memhash64 doesn't cause a problem.
|
||||
func TestMemHash64AlignAccess(t *testing.T) {
|
||||
type Key struct {
|
||||
_ [1]byte
|
||||
k [8]byte
|
||||
_ [7]byte
|
||||
}
|
||||
key := Key{}
|
||||
sink = (uint64)(MemHash64(unsafe.Pointer(&key.k), 0))
|
||||
}
|
||||
|
||||
func TestMemHash64Equality(t *testing.T) {
|
||||
if *UseAeshash {
|
||||
t.Skip("skipping since AES hash implementation is used")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue