From e1937251847bce7f6ccc149dfd64b34152588fce Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 23 Apr 2020 22:17:58 -0700 Subject: [PATCH] strings: simpler and slightly faster implementation of FieldsFunc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed the need for maintaining an extra variable in one of the inner loops, leading to a slight speed-up for short strings. Benchmarks run on a "quiet" MacBook Pro, 3.3GHz Dual-Core Intel Core i7, with 16GB 2133MHz LPDDR3 RAM running macOS 10.15.4. name old time/op new time/op delta FieldsFunc/ASCII/16-4 147ns ± 0% 144ns ± 1% -2.04% (p=0.000 n=4+5) FieldsFunc/ASCII/256-4 1.63µs ± 0% 1.59µs ± 1% -2.50% (p=0.008 n=5+5) FieldsFunc/ASCII/4096-4 30.0µs ± 0% 29.3µs ± 2% ~ (p=0.190 n=4+5) FieldsFunc/ASCII/65536-4 491µs ± 5% 473µs ± 2% ~ (p=0.095 n=5+5) FieldsFunc/ASCII/1048576-4 8.02ms ± 7% 7.85ms ± 4% ~ (p=0.548 n=5+5) FieldsFunc/Mixed/16-4 182ns ± 1% 181ns ± 4% ~ (p=0.357 n=5+5) FieldsFunc/Mixed/256-4 1.74µs ± 1% 1.74µs ± 1% ~ (p=0.881 n=5+5) FieldsFunc/Mixed/4096-4 34.9µs ± 2% 34.7µs ± 0% ~ (p=0.310 n=5+5) FieldsFunc/Mixed/65536-4 595µs ± 1% 589µs ± 2% ~ (p=0.095 n=5+5) FieldsFunc/Mixed/1048576-4 10.1ms ± 3% 9.8ms ± 2% ~ (p=0.095 n=5+5) name old speed new speed delta FieldsFunc/ASCII/16-4 109MB/s ± 1% 111MB/s ± 1% +2.33% (p=0.008 n=5+5) FieldsFunc/ASCII/256-4 157MB/s ± 0% 161MB/s ± 1% +2.57% (p=0.008 n=5+5) FieldsFunc/ASCII/4096-4 137MB/s ± 0% 140MB/s ± 2% ~ (p=0.190 n=4+5) FieldsFunc/ASCII/65536-4 134MB/s ± 4% 139MB/s ± 2% ~ (p=0.095 n=5+5) FieldsFunc/ASCII/1048576-4 131MB/s ± 6% 134MB/s ± 4% ~ (p=0.548 n=5+5) FieldsFunc/Mixed/16-4 87.8MB/s ± 1% 88.3MB/s ± 4% ~ (p=0.421 n=5+5) FieldsFunc/Mixed/256-4 147MB/s ± 1% 147MB/s ± 1% ~ (p=0.841 n=5+5) FieldsFunc/Mixed/4096-4 117MB/s ± 2% 118MB/s ± 0% ~ (p=0.310 n=5+5) FieldsFunc/Mixed/65536-4 110MB/s ± 1% 111MB/s ± 2% ~ (p=0.095 n=5+5) FieldsFunc/Mixed/1048576-4 104MB/s ± 3% 107MB/s ± 2% ~ (p=0.095 n=5+5) name old alloc/op new alloc/op delta FieldsFunc/ASCII/16-4 32.0B ± 0% 32.0B ± 0% ~ (all equal) FieldsFunc/ASCII/256-4 352B ± 0% 352B ± 0% ~ (all equal) FieldsFunc/ASCII/4096-4 21.9kB ± 0% 21.9kB ± 0% ~ (all equal) FieldsFunc/ASCII/65536-4 448kB ± 0% 448kB ± 0% ~ (all equal) FieldsFunc/ASCII/1048576-4 8.85MB ± 0% 8.85MB ± 0% ~ (p=0.738 n=5+5) FieldsFunc/Mixed/16-4 48.0B ± 0% 48.0B ± 0% ~ (all equal) FieldsFunc/Mixed/256-4 416B ± 0% 416B ± 0% ~ (all equal) FieldsFunc/Mixed/4096-4 21.5kB ± 0% 21.5kB ± 0% ~ (all equal) FieldsFunc/Mixed/65536-4 448kB ± 0% 448kB ± 0% ~ (all equal) FieldsFunc/Mixed/1048576-4 8.85MB ± 0% 8.85MB ± 0% ~ (p=0.690 n=5+5) name old allocs/op new allocs/op delta FieldsFunc/ASCII/16-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) FieldsFunc/ASCII/256-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) FieldsFunc/ASCII/4096-4 5.00 ± 0% 5.00 ± 0% ~ (all equal) FieldsFunc/ASCII/65536-4 12.0 ± 0% 12.0 ± 0% ~ (all equal) FieldsFunc/ASCII/1048576-4 24.0 ± 0% 24.0 ± 0% ~ (all equal) FieldsFunc/Mixed/16-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) FieldsFunc/Mixed/256-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) FieldsFunc/Mixed/4096-4 5.00 ± 0% 5.00 ± 0% ~ (all equal) FieldsFunc/Mixed/65536-4 12.0 ± 0% 12.0 ± 0% ~ (all equal) FieldsFunc/Mixed/1048576-4 24.0 ± 0% 24.0 ± 0% ~ (all equal) Change-Id: I06828d798ca1a624a26edd7f7b68c3bf2fc28f84 Reviewed-on: https://go-review.googlesource.com/c/go/+/229765 Reviewed-by: Martin Möhrmann --- src/strings/strings.go | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/strings/strings.go b/src/strings/strings.go index 5f244d6e20..88fbeecc6f 100644 --- a/src/strings/strings.go +++ b/src/strings/strings.go @@ -379,25 +379,29 @@ func FieldsFunc(s string, f func(rune) bool) []string { spans := make([]span, 0, 32) // Find the field start and end indices. - wasField := false - fromIndex := 0 - for i, rune := range s { + // Doing this in a separate pass (rather than slicing the string s + // and collecting the result substrings right away) is significantly + // more efficient, possibly due to cache effects. + start := -1 // valid span start if >= 0 + for end, rune := range s { if f(rune) { - if wasField { - spans = append(spans, span{start: fromIndex, end: i}) - wasField = false + if start >= 0 { + spans = append(spans, span{start, end}) + // Set start to a negative value. + // Note: using -1 here consistently and reproducibly + // slows down this code by a several percent on amd64. + start = ^start } } else { - if !wasField { - fromIndex = i - wasField = true + if start < 0 { + start = end } } } // Last field might end at EOF. - if wasField { - spans = append(spans, span{fromIndex, len(s)}) + if start >= 0 { + spans = append(spans, span{start, len(s)}) } // Create strings from recorded field indices.