]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: faster string equality.
authorRémy Oudompheng <oudomphe@phare.normalesup.org>
Sun, 5 Aug 2012 19:35:41 +0000 (21:35 +0200)
committerRémy Oudompheng <oudomphe@phare.normalesup.org>
Sun, 5 Aug 2012 19:35:41 +0000 (21:35 +0200)
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

src/cmd/gc/builtin.c
src/cmd/gc/runtime.go
src/cmd/gc/walk.c
src/pkg/runtime/alg.c
src/pkg/runtime/string.goc
src/pkg/runtime/string_test.go [new file with mode: 0644]

index e17aa7953ae445a133a51239722e9d48f630f69f..535e38fac58d713d3df53954a1f82ea5427e4d19 100644 (file)
@@ -27,6 +27,7 @@ char *runtimeimport =
        "func @\"\".appendslice(@\"\".typ *byte, @\"\".x any, @\"\".y []any) (? any)\n"
        "func @\"\".appendstr(@\"\".typ *byte, @\"\".x []byte, @\"\".y string) (? []byte)\n"
        "func @\"\".cmpstring(? string, ? string) (? int)\n"
+       "func @\"\".eqstring(? string, ? string) (? bool)\n"
        "func @\"\".slicestring(? string, ? int, ? int) (? string)\n"
        "func @\"\".slicestring1(? string, ? int) (? string)\n"
        "func @\"\".intstring(? int64) (? string)\n"
index 91fb7720f50714295bc6951f10f032976c726fb2..408f624cff5688ad17fa0816f0aaab6c6ce78f9e 100644 (file)
@@ -45,6 +45,7 @@ func appendslice(typ *byte, x any, y []any) any
 func appendstr(typ *byte, x []byte, y string) []byte
 
 func cmpstring(string, string) int
+func eqstring(string, string) bool
 func slicestring(string, int, int) string
 func slicestring1(string, int) string
 func intstring(int64) string
index 20f8bbfe93b7fee5e0d47e408524d19e78d8e35e..4855b13ba943e5b4f2bc8cd61caa2555d1e86287 100644 (file)
@@ -1021,27 +1021,34 @@ walkexpr(Node **np, NodeList **init)
                        goto ret;
                }
 
-               // prepare for rewrite below
                if(n->etype == OEQ || n->etype == ONE) {
+                       // prepare for rewrite below
                        n->left = cheapexpr(n->left, init);
                        n->right = cheapexpr(n->right, init);
-               }
 
-               // 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));
+                       r = mkcall("eqstring", types[TBOOL], init,
+                               conv(n->left, types[TSTRING]),
+                               conv(n->right, types[TSTRING]));
 
-               // quick check of len before full compare for == or !=
-               if(n->etype == OEQ || n->etype == ONE) {
-                       if(n->etype == OEQ)
+                       // quick check of len before full compare for == or !=
+                       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);
-                       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);
+                       }
                        typecheck(&r, Erv);
                        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);
                if(n->type->etype != TBOOL) fatal("cmp %T", n->type);
                r->type = n->type;
index bc848da38cd109345f2cf92120ec496e2f664ec2..ce872755ff29ccd558a33e41e872b6f97cf97b22 100644 (file)
@@ -324,6 +324,10 @@ runtime·strequal(bool *eq, uintptr s, void *a, void *b)
                *eq = false;
                return;
        }
+       if(((String*)a)->str == ((String*)b)->str) {
+               *eq = true;
+               return;
+       }
        runtime·memequal(eq, alen, ((String*)a)->str, ((String*)b)->str);
 }
 
index 7cab6d241707941352a76456d2e61ae3739a2c80..b72a1aa5e7578edb41760d085f368091de7c3a54 100644 (file)
@@ -204,6 +204,26 @@ func cmpstring(s1 String, s2 String) (v int32) {
        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
 runtime·strcmp(byte *s1, byte *s2)
 {
diff --git a/src/pkg/runtime/string_test.go b/src/pkg/runtime/string_test.go
new file mode 100644 (file)
index 0000000..8f13f0f
--- /dev/null
@@ -0,0 +1,45 @@
+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")
+               }
+       }
+}