Commit 3216e0ce authored by Martin Möhrmann's avatar Martin Möhrmann

cmd/compile: replace eqstring with memequal

eqstring is only called for strings with equal lengths.
Instead of pushing a pointer and length for each argument string
on the stack we can omit pushing one of the lengths on the stack.

Changing eqstrings signature to eqstring(*uint8, *uint8, int) bool
to implement the above optimization would make it very similar to the
existing memequal(*any, *any, uintptr) bool function.

Since string lengths are positive we can avoid code redundancy and
use memequal instead of using eqstring with an optimized signature.

go command binary size reduced by 4128 bytes on amd64.

name                          old time/op    new time/op    delta
CompareStringEqual              6.03ns ± 1%    5.71ns ± 1%   -5.23%  (p=0.000 n=19+18)
CompareStringIdentical          2.88ns ± 1%    3.22ns ± 7%  +11.86%  (p=0.000 n=20+20)
CompareStringSameLength         4.31ns ± 1%    4.01ns ± 1%   -7.17%  (p=0.000 n=19+19)
CompareStringDifferentLength    0.29ns ± 2%    0.29ns ± 2%     ~     (p=1.000 n=20+20)
CompareStringBigUnaligned       64.3µs ± 2%    64.1µs ± 3%     ~     (p=0.164 n=20+19)
CompareStringBig                61.9µs ± 1%    61.6µs ± 2%   -0.46%  (p=0.033 n=20+19)

Change-Id: Ice15f3b937c981f0d3bc8479a9ea0d10658ac8df
Reviewed-on: https://go-review.googlesource.com/53650
Run-TryBot: Martin Möhrmann <moehrmann@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarKeith Randall <khr@golang.org>
parent d05a1238
This diff is collapsed.
...@@ -48,7 +48,6 @@ func concatstring5(*[32]byte, string, string, string, string, string) string ...@@ -48,7 +48,6 @@ func concatstring5(*[32]byte, string, string, string, string, string) string
func concatstrings(*[32]byte, []string) string func concatstrings(*[32]byte, []string) string
func cmpstring(string, string) int func cmpstring(string, string) int
func eqstring(string, string) bool
func intstring(*[4]byte, int64) string func intstring(*[4]byte, int64) string
func slicebytetostring(*[32]byte, []byte) string func slicebytetostring(*[32]byte, []byte) string
func slicebytetostringtmp([]byte) string func slicebytetostringtmp([]byte) string
......
...@@ -1369,18 +1369,27 @@ opswitch: ...@@ -1369,18 +1369,27 @@ opswitch:
n.Left = cheapexpr(n.Left, init) n.Left = cheapexpr(n.Left, init)
n.Right = cheapexpr(n.Right, init) n.Right = cheapexpr(n.Right, init)
r = mkcall("eqstring", types.Types[TBOOL], init, conv(n.Left, types.Types[TSTRING]), conv(n.Right, types.Types[TSTRING])) lstr := conv(n.Left, types.Types[TSTRING])
rstr := conv(n.Right, types.Types[TSTRING])
// quick check of len before full compare for == or != lptr := nod(OSPTR, lstr, nil)
// eqstring assumes that the lengths are equal rptr := nod(OSPTR, rstr, nil)
llen := conv(nod(OLEN, lstr, nil), types.Types[TUINTPTR])
rlen := conv(nod(OLEN, rstr, nil), types.Types[TUINTPTR])
fn := syslook("memequal")
fn = substArgTypes(fn, types.Types[TUINT8], types.Types[TUINT8])
r = mkcall1(fn, types.Types[TBOOL], init, lptr, rptr, llen)
// quick check of len before full compare for == or !=.
// memequal then tests equality up to length len.
// TODO(marvin): Fix Node.EType type union. // TODO(marvin): Fix Node.EType type union.
if Op(n.Etype) == OEQ { if Op(n.Etype) == OEQ {
// len(left) == len(right) && eqstring(left, right) // len(left) == len(right) && memequal(left, right, len)
r = nod(OANDAND, nod(OEQ, nod(OLEN, n.Left, nil), nod(OLEN, n.Right, nil)), r) r = nod(OANDAND, nod(OEQ, llen, rlen), r)
} else { } else {
// len(left) != len(right) || !eqstring(left, right) // len(left) != len(right) || !memequal(left, right, len)
r = nod(ONOT, r, nil) r = nod(ONOT, r, nil)
r = nod(OOROR, nod(ONE, nod(OLEN, n.Left, nil), nod(OLEN, n.Right, nil)), r) r = nod(OOROR, nod(ONE, llen, rlen), r)
} }
r = typecheck(r, Erv) r = typecheck(r, Erv)
......
...@@ -1306,23 +1306,6 @@ eq: ...@@ -1306,23 +1306,6 @@ eq:
MOVB $1, ret+8(FP) MOVB $1, ret+8(FP)
RET RET
// eqstring tests whether two strings are equal.
// The compiler guarantees that strings passed
// to eqstring have equal length.
// See runtime_test.go:eqstring_generic for
// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$0-17
MOVL s1_base+0(FP), SI
MOVL s2_base+8(FP), DI
CMPL SI, DI
JEQ same
MOVL s1_len+4(FP), BX
LEAL ret+16(FP), AX
JMP runtime·memeqbody(SB)
same:
MOVB $1, ret+16(FP)
RET
TEXT bytes·Equal(SB),NOSPLIT,$0-25 TEXT bytes·Equal(SB),NOSPLIT,$0-25
MOVL a_len+4(FP), BX MOVL a_len+4(FP), BX
MOVL b_len+16(FP), CX MOVL b_len+16(FP), CX
......
...@@ -1326,23 +1326,6 @@ eq: ...@@ -1326,23 +1326,6 @@ eq:
MOVB $1, ret+16(FP) MOVB $1, ret+16(FP)
RET RET
// eqstring tests whether two strings are equal.
// The compiler guarantees that strings passed
// to eqstring have equal length.
// See runtime_test.go:eqstring_generic for
// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$0-33
MOVQ s1_base+0(FP), SI
MOVQ s2_base+16(FP), DI
CMPQ SI, DI
JEQ eq
MOVQ s1_len+8(FP), BX
LEAQ ret+32(FP), AX
JMP runtime·memeqbody(SB)
eq:
MOVB $1, ret+32(FP)
RET
// a in SI // a in SI
// b in DI // b in DI
// count in BX // count in BX
......
...@@ -641,24 +641,6 @@ eq: ...@@ -641,24 +641,6 @@ eq:
MOVB $1, ret+8(FP) MOVB $1, ret+8(FP)
RET RET
// eqstring tests whether two strings are equal.
// The compiler guarantees that strings passed
// to eqstring have equal length.
// See runtime_test.go:eqstring_generic for
// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$0-17
MOVL s1_base+0(FP), SI
MOVL s2_base+8(FP), DI
CMPL SI, DI
JEQ same
MOVL s1_len+4(FP), BX
CALL runtime·memeqbody(SB)
MOVB AX, ret+16(FP)
RET
same:
MOVB $1, ret+16(FP)
RET
// a in SI // a in SI
// b in DI // b in DI
// count in BX // count in BX
......
...@@ -813,31 +813,6 @@ samebytes: ...@@ -813,31 +813,6 @@ samebytes:
MOVW R0, (R7) MOVW R0, (R7)
RET RET
// eqstring tests whether two strings are equal.
// The compiler guarantees that strings passed
// to eqstring have equal length.
// See runtime_test.go:eqstring_generic for
// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$-4-17
MOVW s1_base+0(FP), R2
MOVW s2_base+8(FP), R3
MOVW $1, R8
MOVB R8, ret+16(FP)
CMP R2, R3
RET.EQ
MOVW s1_len+4(FP), R0
ADD R2, R0, R6
loop:
CMP R2, R6
RET.EQ
MOVBU.P 1(R2), R4
MOVBU.P 1(R3), R5
CMP R4, R5
BEQ loop
MOVW $0, R8
MOVB R8, ret+16(FP)
RET
// TODO: share code with memequal? // TODO: share code with memequal?
TEXT bytes·Equal(SB),NOSPLIT,$0-25 TEXT bytes·Equal(SB),NOSPLIT,$0-25
MOVW a_len+4(FP), R1 MOVW a_len+4(FP), R1
......
...@@ -806,31 +806,6 @@ samebytes: ...@@ -806,31 +806,6 @@ samebytes:
MOVD R4, (R7) MOVD R4, (R7)
RET RET
// eqstring tests whether two strings are equal.
// The compiler guarantees that strings passed
// to eqstring have equal length.
// See runtime_test.go:eqstring_generic for
// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$0-33
MOVD s1_base+0(FP), R0
MOVD s1_len+8(FP), R1
MOVD s2_base+16(FP), R2
ADD R0, R1 // end
loop:
CMP R0, R1
BEQ equal // reaches the end
MOVBU.P 1(R0), R4
MOVBU.P 1(R2), R5
CMP R4, R5
BEQ loop
notequal:
MOVB ZR, ret+32(FP)
RET
equal:
MOVD $1, R0
MOVB R0, ret+32(FP)
RET
// //
// functions for other packages // functions for other packages
// //
......
...@@ -679,31 +679,6 @@ eq: ...@@ -679,31 +679,6 @@ eq:
MOVB R1, ret+16(FP) MOVB R1, ret+16(FP)
RET RET
// eqstring tests whether two strings are equal.
// The compiler guarantees that strings passed
// to eqstring have equal length.
// See runtime_test.go:eqstring_generic for
// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$0-33
MOVV s1_base+0(FP), R1
MOVV s2_base+16(FP), R2
MOVV $1, R3
MOVB R3, ret+32(FP)
BNE R1, R2, 2(PC)
RET
MOVV s1_len+8(FP), R3
ADDV R1, R3, R4
loop:
BNE R1, R4, 2(PC)
RET
MOVBU (R1), R6
ADDV $1, R1
MOVBU (R2), R7
ADDV $1, R2
BEQ R6, R7, loop
MOVB R0, ret+32(FP)
RET
// TODO: share code with memequal? // TODO: share code with memequal?
TEXT bytes·Equal(SB),NOSPLIT,$0-49 TEXT bytes·Equal(SB),NOSPLIT,$0-49
MOVV a_len+8(FP), R3 MOVV a_len+8(FP), R3
......
...@@ -695,31 +695,6 @@ eq: ...@@ -695,31 +695,6 @@ eq:
MOVB R1, ret+8(FP) MOVB R1, ret+8(FP)
RET RET
// eqstring tests whether two strings are equal.
// The compiler guarantees that strings passed
// to eqstring have equal length.
// See runtime_test.go:eqstring_generic for
// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$0-17
MOVW s1_base+0(FP), R1
MOVW s2_base+8(FP), R2
MOVW $1, R3
MOVBU R3, ret+16(FP)
BNE R1, R2, 2(PC)
RET
MOVW s1_len+4(FP), R3
ADDU R1, R3, R4
loop:
BNE R1, R4, 2(PC)
RET
MOVBU (R1), R6
ADDU $1, R1
MOVBU (R2), R7
ADDU $1, R2
BEQ R6, R7, loop
MOVB R0, ret+16(FP)
RET
TEXT bytes·Equal(SB),NOSPLIT,$0-25 TEXT bytes·Equal(SB),NOSPLIT,$0-25
MOVW a_len+4(FP), R3 MOVW a_len+4(FP), R3
MOVW b_len+16(FP), R4 MOVW b_len+16(FP), R4
......
...@@ -1057,24 +1057,6 @@ equal: ...@@ -1057,24 +1057,6 @@ equal:
MOVD $1, R9 MOVD $1, R9
RET RET
// eqstring tests whether two strings are equal.
// The compiler guarantees that strings passed
// to eqstring have equal length.
// See runtime_test.go:eqstring_generic for
// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT,$0-33
MOVD s1_base+0(FP), R3
MOVD s2_base+16(FP), R4
MOVD $1, R5
MOVB R5, ret+32(FP)
CMP R3, R4
BNE 2(PC)
RET
MOVD s1_len+8(FP), R5
BL runtime·memeqbody(SB)
MOVB R9, ret+32(FP)
RET
TEXT bytes·Equal(SB),NOSPLIT,$0-49 TEXT bytes·Equal(SB),NOSPLIT,$0-49
MOVD a_len+8(FP), R4 MOVD a_len+8(FP), R4
MOVD b_len+32(FP), R5 MOVD b_len+32(FP), R5
......
...@@ -704,18 +704,6 @@ TEXT runtime·memequal_varlen(SB),NOSPLIT|NOFRAME,$0-17 ...@@ -704,18 +704,6 @@ TEXT runtime·memequal_varlen(SB),NOSPLIT|NOFRAME,$0-17
LA ret+16(FP), R7 LA ret+16(FP), R7
BR runtime·memeqbody(SB) BR runtime·memeqbody(SB)
// eqstring tests whether two strings are equal.
// The compiler guarantees that strings passed
// to eqstring have equal length.
// See runtime_test.go:eqstring_generic for
// equivalent Go code.
TEXT runtime·eqstring(SB),NOSPLIT|NOFRAME,$0-33
MOVD s1_base+0(FP), R3
MOVD s1_len+8(FP), R6
MOVD s2_base+16(FP), R5
LA ret+32(FP), R7
BR runtime·memeqbody(SB)
TEXT bytes·Equal(SB),NOSPLIT|NOFRAME,$0-49 TEXT bytes·Equal(SB),NOSPLIT|NOFRAME,$0-49
MOVD a_len+8(FP), R2 MOVD a_len+8(FP), R2
MOVD b_len+32(FP), R6 MOVD b_len+32(FP), R6
......
...@@ -196,9 +196,9 @@ func eqstring_generic(s1, s2 string) bool { ...@@ -196,9 +196,9 @@ func eqstring_generic(s1, s2 string) bool {
} }
func TestEqString(t *testing.T) { func TestEqString(t *testing.T) {
// This isn't really an exhaustive test of eqstring, it's // This isn't really an exhaustive test of == on strings, it's
// just a convenient way of documenting (via eqstring_generic) // just a convenient way of documenting (via eqstring_generic)
// what eqstring does. // what == does.
s := []string{ s := []string{
"", "",
"a", "a",
...@@ -213,7 +213,7 @@ func TestEqString(t *testing.T) { ...@@ -213,7 +213,7 @@ func TestEqString(t *testing.T) {
x := s1 == s2 x := s1 == s2
y := eqstring_generic(s1, s2) y := eqstring_generic(s1, s2)
if x != y { if x != y {
t.Errorf(`eqstring("%s","%s") = %t, want %t`, s1, s2, x, y) t.Errorf(`("%s" == "%s") = %t, want %t`, s1, s2, x, y)
} }
} }
} }
......
...@@ -300,7 +300,6 @@ func round(n, a uintptr) uintptr { ...@@ -300,7 +300,6 @@ func round(n, a uintptr) uintptr {
func checkASM() bool func checkASM() bool
func memequal_varlen(a, b unsafe.Pointer) bool func memequal_varlen(a, b unsafe.Pointer) bool
func eqstring(s1, s2 string) bool
// bool2int returns 0 if x is false or 1 if x is true. // bool2int returns 0 if x is false or 1 if x is true.
func bool2int(x bool) int { func bool2int(x bool) int {
......
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