]> Cypherpunks repositories - gostls13.git/commitdiff
strings: make IndexRune faster
authorHiroshi Ioka <hirochachacha@gmail.com>
Tue, 6 Sep 2016 11:23:40 +0000 (20:23 +0900)
committerBrad Fitzpatrick <bradfitz@golang.org>
Wed, 7 Sep 2016 01:03:10 +0000 (01:03 +0000)
re-implement IndexRune by Index which is well optimized to get
performance gain.

name                   old time/op  new time/op  delta
IndexRune-4            30.2ns ± 1%  28.3ns ± 1%   -6.22%  (p=0.000 n=20+19)
IndexRuneLongString-4   156ns ± 1%    49ns ± 1%  -68.72%  (p=0.000 n=19+19)
IndexRuneFastPath-4    10.6ns ± 2%  10.0ns ± 1%   -6.30%  (p=0.000 n=18+18)

Change-Id: Ie663b8f7860ca51892dd4be182fca3caa5f8ae61
Reviewed-on: https://go-review.googlesource.com/28546
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/strings/strings.go
src/strings/strings_amd64.go
src/strings/strings_test.go

index 738c49303259171f6918643a2d529d5b67bbd375..c5355db9a27da8ede93e48c5077003aba825af7b 100644 (file)
@@ -176,17 +176,11 @@ func LastIndex(s, sep string) int {
 // IndexRune returns the index of the first instance of the Unicode code point
 // r, or -1 if rune is not present in s.
 func IndexRune(s string, r rune) int {
-       switch {
-       case r < utf8.RuneSelf:
+       if r < utf8.RuneSelf {
                return IndexByte(s, byte(r))
-       default:
-               for i, c := range s {
-                       if c == r {
-                               return i
-                       }
-               }
        }
-       return -1
+
+       return Index(s, string(r))
 }
 
 // IndexAny returns the index of the first instance of any Unicode code point
index 55bf2d2f6fc77a05eb4f3f7dbecbefb121b1c80f..434e2e9eb3ad5d61e0de00e858557963fe0954c0 100644 (file)
@@ -4,6 +4,8 @@
 
 package strings
 
+//go:noescape
+
 // indexShortStr returns the index of the first instance of c in s, or -1 if c is not present in s.
 // indexShortStr requires 2 <= len(c) <= shortStringLen
 func indexShortStr(s, c string) int // ../runtime/asm_$GOARCH.s
index fcef761da798a4fd892634ac366a70100d33f544..5fdf59c88d9994192e845330de9902798480d5fb 100644 (file)
@@ -244,6 +244,20 @@ func TestIndexRune(t *testing.T) {
                        t.Errorf("IndexRune(%q,%d)= %v; want %v", test.s, test.rune, actual, test.out)
                }
        }
+
+       haystack := "test世界"
+       allocs := testing.AllocsPerRun(1000, func() {
+               if i := IndexRune(haystack, 's'); i != 2 {
+                       t.Fatalf("'s' at %d; want 2", i)
+               }
+               if i := IndexRune(haystack, '世'); i != 4 {
+                       t.Fatalf("'世' at %d; want 4", i)
+               }
+       })
+
+       if allocs != 0 {
+               t.Errorf(`expected no allocations, got %f`, allocs)
+       }
 }
 
 const benchmarkString = "some_text=some☺value"
@@ -257,6 +271,17 @@ func BenchmarkIndexRune(b *testing.B) {
        }
 }
 
+var benchmarkLongString = Repeat(" ", 100) + benchmarkString
+
+func BenchmarkIndexRuneLongString(b *testing.B) {
+       if got := IndexRune(benchmarkLongString, '☺'); got != 114 {
+               b.Fatalf("wrong index: expected 114, got=%d", got)
+       }
+       for i := 0; i < b.N; i++ {
+               IndexRune(benchmarkLongString, '☺')
+       }
+}
+
 func BenchmarkIndexRuneFastPath(b *testing.B) {
        if got := IndexRune(benchmarkString, 'v'); got != 17 {
                b.Fatalf("wrong index: expected 17, got=%d", got)