Commit 77f3e189 authored by Rémy Oudompheng's avatar Rémy Oudompheng

runtime: faster string equality.

benchmark                                old ns/op    new ns/op    delta
BenchmarkCompareStringEqual                     51           35  -30.20%
BenchmarkCompareStringIdentical                 51            7  -85.71%
BenchmarkCompareStringSameLength                25           18  -28.29%
BenchmarkCompareStringDifferentLength            2            2   +1.46%

R=golang-dev, rsc
CC=golang-dev, remy
https://golang.org/cl/6450092
parent ab058b35
...@@ -27,6 +27,7 @@ char *runtimeimport = ...@@ -27,6 +27,7 @@ char *runtimeimport =
"func @\"\".appendslice(@\"\".typ *byte, @\"\".x any, @\"\".y []any) (? any)\n" "func @\"\".appendslice(@\"\".typ *byte, @\"\".x any, @\"\".y []any) (? any)\n"
"func @\"\".appendstr(@\"\".typ *byte, @\"\".x []byte, @\"\".y string) (? []byte)\n" "func @\"\".appendstr(@\"\".typ *byte, @\"\".x []byte, @\"\".y string) (? []byte)\n"
"func @\"\".cmpstring(? string, ? string) (? int)\n" "func @\"\".cmpstring(? string, ? string) (? int)\n"
"func @\"\".eqstring(? string, ? string) (? bool)\n"
"func @\"\".slicestring(? string, ? int, ? int) (? string)\n" "func @\"\".slicestring(? string, ? int, ? int) (? string)\n"
"func @\"\".slicestring1(? string, ? int) (? string)\n" "func @\"\".slicestring1(? string, ? int) (? string)\n"
"func @\"\".intstring(? int64) (? string)\n" "func @\"\".intstring(? int64) (? string)\n"
......
...@@ -45,6 +45,7 @@ func appendslice(typ *byte, x any, y []any) any ...@@ -45,6 +45,7 @@ func appendslice(typ *byte, x any, y []any) any
func appendstr(typ *byte, x []byte, y string) []byte func appendstr(typ *byte, x []byte, y string) []byte
func cmpstring(string, string) int func cmpstring(string, string) int
func eqstring(string, string) bool
func slicestring(string, int, int) string func slicestring(string, int, int) string
func slicestring1(string, int) string func slicestring1(string, int) string
func intstring(int64) string func intstring(int64) string
......
...@@ -1021,27 +1021,34 @@ walkexpr(Node **np, NodeList **init) ...@@ -1021,27 +1021,34 @@ walkexpr(Node **np, NodeList **init)
goto ret; goto ret;
} }
// prepare for rewrite below
if(n->etype == OEQ || n->etype == ONE) { if(n->etype == OEQ || n->etype == ONE) {
// prepare for rewrite below
n->left = cheapexpr(n->left, init); n->left = cheapexpr(n->left, init);
n->right = cheapexpr(n->right, init); n->right = cheapexpr(n->right, init);
}
// sys_cmpstring(s1, s2) :: 0 r = mkcall("eqstring", types[TBOOL], init,
r = mkcall("cmpstring", types[TINT], init, conv(n->left, types[TSTRING]),
conv(n->left, types[TSTRING]), conv(n->right, types[TSTRING]));
conv(n->right, types[TSTRING]));
r = nod(n->etype, r, nodintconst(0));
// quick check of len before full compare for == or != // quick check of len before full compare for == or !=
if(n->etype == OEQ || n->etype == ONE) { if(n->etype == OEQ) {
if(n->etype == OEQ) // len(left) == len(right) && eqstring(left, right)
r = nod(OANDAND, nod(OEQ, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); r = nod(OANDAND, nod(OEQ, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r);
else } else {
// len(left) != len(right) || !eqstring(left, right)
r = nod(ONOT, r, N);
r = nod(OOROR, nod(ONE, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); r = nod(OOROR, nod(ONE, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r);
}
typecheck(&r, Erv); typecheck(&r, Erv);
walkexpr(&r, nil); walkexpr(&r, nil);
} else {
// sys_cmpstring(s1, s2) :: 0
r = mkcall("cmpstring", types[TINT], init,
conv(n->left, types[TSTRING]),
conv(n->right, types[TSTRING]));
r = nod(n->etype, r, nodintconst(0));
} }
typecheck(&r, Erv); typecheck(&r, Erv);
if(n->type->etype != TBOOL) fatal("cmp %T", n->type); if(n->type->etype != TBOOL) fatal("cmp %T", n->type);
r->type = n->type; r->type = n->type;
......
...@@ -324,6 +324,10 @@ runtime·strequal(bool *eq, uintptr s, void *a, void *b) ...@@ -324,6 +324,10 @@ runtime·strequal(bool *eq, uintptr s, void *a, void *b)
*eq = false; *eq = false;
return; return;
} }
if(((String*)a)->str == ((String*)b)->str) {
*eq = true;
return;
}
runtime·memequal(eq, alen, ((String*)a)->str, ((String*)b)->str); runtime·memequal(eq, alen, ((String*)a)->str, ((String*)b)->str);
} }
......
...@@ -204,6 +204,26 @@ func cmpstring(s1 String, s2 String) (v int32) { ...@@ -204,6 +204,26 @@ func cmpstring(s1 String, s2 String) (v int32) {
v = cmpstring(s1, s2); v = cmpstring(s1, s2);
} }
func eqstring(s1 String, s2 String) (v bool) {
uint32 i, l;
if(s1.len != s2.len) {
v = false;
return;
}
if(s1.str == s2.str) {
v = true;
return;
}
l = s1.len;
for(i=0; i<l; i++)
if(s1.str[i] != s2.str[i]) {
v = false;
return;
}
v = true;
}
int32 int32
runtime·strcmp(byte *s1, byte *s2) runtime·strcmp(byte *s1, byte *s2)
{ {
......
package runtime_test
import (
"testing"
)
func BenchmarkCompareStringEqual(b *testing.B) {
bytes := []byte("Hello Gophers!")
s1, s2 := string(bytes), string(bytes)
for i := 0; i < b.N; i++ {
if s1 != s2 {
b.Fatal("s1 != s2")
}
}
}
func BenchmarkCompareStringIdentical(b *testing.B) {
s1 := "Hello Gophers!"
s2 := s1
for i := 0; i < b.N; i++ {
if s1 != s2 {
b.Fatal("s1 != s2")
}
}
}
func BenchmarkCompareStringSameLength(b *testing.B) {
s1 := "Hello Gophers!"
s2 := "Hello, Gophers"
for i := 0; i < b.N; i++ {
if s1 == s2 {
b.Fatal("s1 == s2")
}
}
}
func BenchmarkCompareStringDifferentLength(b *testing.B) {
s1 := "Hello Gophers!"
s2 := "Hello, Gophers!"
for i := 0; i < b.N; i++ {
if s1 == s2 {
b.Fatal("s1 == s2")
}
}
}
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