From 8737dac1f27db2596f1d24aab8e5c942734c3bb4 Mon Sep 17 00:00:00 2001 From: Hiroshi Ioka Date: Tue, 6 Sep 2016 20:23:40 +0900 Subject: [PATCH] strings: make IndexRune faster MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/strings/strings.go | 12 +++--------- src/strings/strings_amd64.go | 2 ++ src/strings/strings_test.go | 25 +++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/strings/strings.go b/src/strings/strings.go index 738c493032..c5355db9a2 100644 --- a/src/strings/strings.go +++ b/src/strings/strings.go @@ -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 diff --git a/src/strings/strings_amd64.go b/src/strings/strings_amd64.go index 55bf2d2f6f..434e2e9eb3 100644 --- a/src/strings/strings_amd64.go +++ b/src/strings/strings_amd64.go @@ -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 diff --git a/src/strings/strings_test.go b/src/strings/strings_test.go index fcef761da7..5fdf59c88d 100644 --- a/src/strings/strings_test.go +++ b/src/strings/strings_test.go @@ -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) -- 2.48.1