Commit 8737dac1 authored by Hiroshi Ioka's avatar Hiroshi Ioka Committed by Brad Fitzpatrick

strings: make IndexRune faster

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/28546Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent a4bdd645
...@@ -176,17 +176,11 @@ func LastIndex(s, sep string) int { ...@@ -176,17 +176,11 @@ func LastIndex(s, sep string) int {
// IndexRune returns the index of the first instance of the Unicode code point // IndexRune returns the index of the first instance of the Unicode code point
// r, or -1 if rune is not present in s. // r, or -1 if rune is not present in s.
func IndexRune(s string, r rune) int { func IndexRune(s string, r rune) int {
switch { if r < utf8.RuneSelf {
case r < utf8.RuneSelf:
return IndexByte(s, byte(r)) 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 // IndexAny returns the index of the first instance of any Unicode code point
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
package strings 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 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 // indexShortStr requires 2 <= len(c) <= shortStringLen
func indexShortStr(s, c string) int // ../runtime/asm_$GOARCH.s func indexShortStr(s, c string) int // ../runtime/asm_$GOARCH.s
......
...@@ -244,6 +244,20 @@ func TestIndexRune(t *testing.T) { ...@@ -244,6 +244,20 @@ func TestIndexRune(t *testing.T) {
t.Errorf("IndexRune(%q,%d)= %v; want %v", test.s, test.rune, actual, test.out) 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" const benchmarkString = "some_text=some☺value"
...@@ -257,6 +271,17 @@ func BenchmarkIndexRune(b *testing.B) { ...@@ -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) { func BenchmarkIndexRuneFastPath(b *testing.B) {
if got := IndexRune(benchmarkString, 'v'); got != 17 { if got := IndexRune(benchmarkString, 'v'); got != 17 {
b.Fatalf("wrong index: expected 17, got=%d", got) b.Fatalf("wrong index: expected 17, got=%d", got)
......
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