Commit bd70bd9c authored by Keith Randall's avatar Keith Randall

runtime: unify memeq and memequal

They do the same thing, except memequal also has the short-circuit
check if the two pointers are equal.

A) We might as well always do the short-circuit check, it is only 2 instructions.
B) The extra function call (memequal->memeq) is expensive.

benchmark                 old ns/op     new ns/op     delta
BenchmarkArrayEqual-8     8.56          5.31          -37.97%

No noticeable affect on the former memeq user (maps).

Fixes #14302

Change-Id: I85d1ada59ed11e64dd6c54667f79d32cc5f81948
Reviewed-on: https://go-review.googlesource.com/19843
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 1e00cc16
......@@ -172,13 +172,6 @@ func nilinterhash(p unsafe.Pointer, h uintptr) uintptr {
}
}
func memequal(p, q unsafe.Pointer, size uintptr) bool {
if p == q {
return true
}
return memeq(p, q, size)
}
func memequal0(p, q unsafe.Pointer) bool {
return true
}
......
......@@ -1239,12 +1239,18 @@ TEXT ·checkASM(SB),NOSPLIT,$0-1
SETEQ ret+0(FP)
RET
TEXT runtime·memeq(SB),NOSPLIT,$0-13
// memequal(p, q unsafe.Pointer, size uintptr) bool
TEXT runtime·memequal(SB),NOSPLIT,$0-13
MOVL a+0(FP), SI
MOVL b+4(FP), DI
CMPL SI, DI
JEQ eq
MOVL size+8(FP), BX
LEAL ret+12(FP), AX
JMP runtime·memeqbody(SB)
eq:
MOVB $1, ret+12(FP)
RET
// memequal_varlen(a, b unsafe.Pointer) bool
TEXT runtime·memequal_varlen(SB),NOSPLIT,$0-9
......
......@@ -1269,12 +1269,18 @@ DATA shifts<>+0xf0(SB)/8, $0x0807060504030201
DATA shifts<>+0xf8(SB)/8, $0xff0f0e0d0c0b0a09
GLOBL shifts<>(SB),RODATA,$256
TEXT runtime·memeq(SB),NOSPLIT,$0-25
// memequal(p, q unsafe.Pointer, size uintptr) bool
TEXT runtime·memequal(SB),NOSPLIT,$0-25
MOVQ a+0(FP), SI
MOVQ b+8(FP), DI
CMPQ SI, DI
JEQ eq
MOVQ size+16(FP), BX
LEAQ ret+24(FP), AX
JMP runtime·memeqbody(SB)
eq:
MOVB $1, ret+24(FP)
RET
// memequal_varlen(a, b unsafe.Pointer) bool
TEXT runtime·memequal_varlen(SB),NOSPLIT,$0-17
......
......@@ -573,13 +573,19 @@ TEXT runtime·aeshash64(SB),NOSPLIT,$0-20
MOVL AX, ret+16(FP)
RET
TEXT runtime·memeq(SB),NOSPLIT,$0-17
// memequal(p, q unsafe.Pointer, size uintptr) bool
TEXT runtime·memequal(SB),NOSPLIT,$0-13
MOVL a+0(FP), SI
MOVL b+4(FP), DI
CMPL SI, DI
JEQ eq
MOVL size+8(FP), BX
CALL runtime·memeqbody(SB)
MOVB AX, ret+16(FP)
RET
eq:
MOVB $1, ret+16(FP)
RET
// memequal_varlen(a, b unsafe.Pointer) bool
TEXT runtime·memequal_varlen(SB),NOSPLIT,$0-9
......
......@@ -750,13 +750,16 @@ TEXT runtime·memhash_varlen(SB),NOSPLIT,$16-12
MOVW R0, ret+8(FP)
RET
TEXT runtime·memeq(SB),NOSPLIT,$-4-13
// memequal(p, q unsafe.Pointer, size uintptr) bool
TEXT runtime·memequal(SB),NOSPLIT,$-4-13
MOVW a+0(FP), R1
MOVW b+4(FP), R2
MOVW size+8(FP), R3
ADD R1, R3, R6
MOVW $1, R0
MOVB R0, ret+12(FP)
CMP R1, R2
RET.EQ
loop:
CMP R1, R6
RET.EQ
......@@ -779,7 +782,7 @@ TEXT runtime·memequal_varlen(SB),NOSPLIT,$16-9
MOVW R0, 4(R13)
MOVW R1, 8(R13)
MOVW R2, 12(R13)
BL runtime·memeq(SB)
BL runtime·memequal(SB)
MOVB 16(R13), R0
MOVB R0, ret+8(FP)
RET
......@@ -866,7 +869,7 @@ loop:
MOVB R8, v+16(FP)
RET
// TODO: share code with memeq?
// TODO: share code with memequal?
TEXT bytes·Equal(SB),NOSPLIT,$0-25
MOVW a_len+4(FP), R1
MOVW b_len+16(FP), R3
......
......@@ -765,13 +765,16 @@ TEXT runtime·memhash_varlen(SB),NOSPLIT,$40-24
MOVD R3, ret+16(FP)
RET
TEXT runtime·memeq(SB),NOSPLIT,$-8-25
// memequal(p, q unsafe.Pointer, size uintptr) bool
TEXT runtime·memequal(SB),NOSPLIT,$-8-25
MOVD a+0(FP), R1
MOVD b+8(FP), R2
MOVD size+16(FP), R3
ADD R1, R3, R6
MOVD $1, R0
MOVB R0, ret+24(FP)
CMP R1, R2
BEQ done
loop:
CMP R1, R6
BEQ done
......@@ -794,7 +797,7 @@ TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17
MOVD R3, 8(RSP)
MOVD R4, 16(RSP)
MOVD R5, 24(RSP)
BL runtime·memeq(SB)
BL runtime·memequal(SB)
MOVBU 32(RSP), R3
MOVB R3, ret+16(FP)
RET
......@@ -929,7 +932,7 @@ notfound:
MOVD R0, ret+24(FP)
RET
// TODO: share code with memeq?
// TODO: share code with memequal?
TEXT bytes·Equal(SB),NOSPLIT,$0-49
MOVD a_len+8(FP), R1
MOVD b_len+32(FP), R3
......
......@@ -647,9 +647,11 @@ TEXT runtime·aeshash64(SB),NOSPLIT,$-8-0
TEXT runtime·aeshashstr(SB),NOSPLIT,$-8-0
MOVW (R0), R1
TEXT runtime·memeq(SB),NOSPLIT,$-8-25
// memequal(p, q unsafe.Pointer, size uintptr) bool
TEXT runtime·memequal(SB),NOSPLIT,$-8-25
MOVV a+0(FP), R1
MOVV b+8(FP), R2
BEQ R1, R2, eq
MOVV size+16(FP), R3
ADDV R1, R3, R4
loop:
......@@ -666,6 +668,10 @@ test:
MOVB R0, ret+24(FP)
RET
eq:
MOVV $1, R1
MOVB R1, ret+24(FP)
RET
// memequal_varlen(a, b unsafe.Pointer) bool
TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17
......@@ -676,7 +682,7 @@ TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17
MOVV R1, 8(R29)
MOVV R2, 16(R29)
MOVV R3, 24(R29)
JAL runtime·memeq(SB)
JAL runtime·memequal(SB)
MOVBU 32(R29), R1
MOVB R1, ret+16(FP)
RET
......@@ -710,7 +716,7 @@ loop:
MOVB R0, ret+32(FP)
RET
// TODO: share code with memeq?
// TODO: share code with memequal?
TEXT bytes·Equal(SB),NOSPLIT,$0-49
MOVV a_len+8(FP), R3
MOVV b_len+32(FP), R4
......
......@@ -795,9 +795,12 @@ TEXT runtime·aeshash64(SB),NOSPLIT|NOFRAME,$0-0
TEXT runtime·aeshashstr(SB),NOSPLIT|NOFRAME,$0-0
MOVW (R0), R1
TEXT runtime·memeq(SB),NOSPLIT|NOFRAME,$0-25
// memequal(p, q unsafe.Pointer, size uintptr) bool
TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25
MOVD a+0(FP), R3
MOVD b+8(FP), R4
CMP R3, R4
BEQ eq
MOVD size+16(FP), R5
SUB $1, R3
SUB $1, R4
......@@ -816,6 +819,10 @@ test:
MOVB R0, ret+24(FP)
RET
eq:
MOVD $1, R1
MOVB R1, ret+24(FP)
RET
// memequal_varlen(a, b unsafe.Pointer) bool
TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17
......@@ -827,7 +834,7 @@ TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17
MOVD R3, FIXED_FRAME+0(R1)
MOVD R4, FIXED_FRAME+8(R1)
MOVD R5, FIXED_FRAME+16(R1)
BL runtime·memeq(SB)
BL runtime·memequal(SB)
MOVBZ FIXED_FRAME+24(R1), R3
MOVB R3, ret+16(FP)
RET
......@@ -864,7 +871,7 @@ loop:
MOVB R0, ret+32(FP)
RET
// TODO: share code with memeq?
// TODO: share code with memequal?
TEXT bytes·Equal(SB),NOSPLIT,$0-49
MOVD a_len+8(FP), R3
MOVD b_len+32(FP), R4
......
......@@ -216,7 +216,7 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer {
if k.len != key.len {
continue
}
if k.str == key.str || memeq(k.str, key.str, uintptr(key.len)) {
if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) {
return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize))
}
}
......@@ -254,7 +254,7 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer {
}
if keymaybe != bucketCnt {
k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+keymaybe*2*sys.PtrSize))
if memeq(k.str, key.str, uintptr(key.len)) {
if memequal(k.str, key.str, uintptr(key.len)) {
return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+keymaybe*uintptr(t.valuesize))
}
}
......@@ -284,7 +284,7 @@ dohash:
if k.len != key.len {
continue
}
if k.str == key.str || memeq(k.str, key.str, uintptr(key.len)) {
if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) {
return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize))
}
}
......@@ -321,7 +321,7 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
if k.len != key.len {
continue
}
if k.str == key.str || memeq(k.str, key.str, uintptr(key.len)) {
if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) {
return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)), true
}
}
......@@ -357,7 +357,7 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
}
if keymaybe != bucketCnt {
k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+keymaybe*2*sys.PtrSize))
if memeq(k.str, key.str, uintptr(key.len)) {
if memequal(k.str, key.str, uintptr(key.len)) {
return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+keymaybe*uintptr(t.valuesize)), true
}
}
......@@ -387,7 +387,7 @@ dohash:
if k.len != key.len {
continue
}
if k.str == key.str || memeq(k.str, key.str, uintptr(key.len)) {
if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) {
return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)), true
}
}
......
......@@ -102,6 +102,17 @@ func BenchmarkRuneIterate2(b *testing.B) {
}
}
func BenchmarkArrayEqual(b *testing.B) {
a1 := [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
a2 := [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
b.ResetTimer()
for i := 0; i < b.N; i++ {
if a1 != a2 {
b.Fatal("not equal")
}
}
}
func TestStringW(t *testing.T) {
strings := []string{
"hello",
......
......@@ -85,7 +85,7 @@ func fastrand1() uint32
// in asm_*.s
//go:noescape
func memeq(a, b unsafe.Pointer, size uintptr) bool
func memequal(a, b unsafe.Pointer, size uintptr) bool
// noescape hides a pointer from escape analysis. noescape is
// the identity function but escape analysis doesn't think the
......
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