]> Cypherpunks repositories - gostls13.git/commitdiff
strings: optimize ToLower
authorAgniva De Sarker <agnivade@yahoo.co.in>
Wed, 8 Nov 2017 03:45:53 +0000 (09:15 +0530)
committerJoe Tsai <joetsai@google.com>
Wed, 8 Nov 2017 21:47:19 +0000 (21:47 +0000)
Handling the ASCII case inline and call unicode.ToLower only
for non-ASCII cases.

Gives good improvements for the ASCII case and minor perf
degrade for non-ASCII case

name                                     old time/op    new time/op    delta
ToLower/#00                                10.8ns ± 1%     9.0ns ± 1%  -16.83%  (p=0.008 n=5+5)
ToLower/abc                                23.3ns ± 4%    12.6ns ± 1%  -46.01%  (p=0.008 n=5+5)
ToLower/AbC123                             91.0ns ± 2%    70.4ns ± 0%  -22.59%  (p=0.008 n=5+5)
ToLower/azAZ09_                             104ns ± 3%      75ns ± 1%  -28.35%  (p=0.008 n=5+5)
ToLower/longStrinGwitHmixofsmaLLandcAps     254ns ± 4%     157ns ± 0%  -38.19%  (p=0.016 n=5+4)
ToLower/LONGⱯSTRINGⱯWITHⱯNONASCIIⱯCHARS     446ns ± 1%     451ns ± 1%     ~     (p=0.056 n=5+5)
ToLower/ⱭⱭⱭⱭⱭ                               345ns ± 1%     348ns ± 0%   +0.93%  (p=0.016 n=5+5)

name                                     old alloc/op   new alloc/op   delta
ToLower/#00                                 0.00B          0.00B          ~     (all equal)
ToLower/abc                                 0.00B          0.00B          ~     (all equal)
ToLower/AbC123                              16.0B ± 0%     16.0B ± 0%     ~     (all equal)
ToLower/azAZ09_                             24.0B ± 0%     16.0B ± 0%  -33.33%  (p=0.008 n=5+5)
ToLower/longStrinGwitHmixofsmaLLandcAps     80.0B ± 0%     64.0B ± 0%  -20.00%  (p=0.008 n=5+5)
ToLower/LONGⱯSTRINGⱯWITHⱯNONASCIIⱯCHARS     96.0B ± 0%     96.0B ± 0%     ~     (all equal)
ToLower/ⱭⱭⱭⱭⱭ                               48.0B ± 0%     48.0B ± 0%     ~     (all equal)

Ran on a machine with Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz

Fixes #17859

Change-Id: Iacc1e6b77e1aedba9447a6e94352606f131ea597
Reviewed-on: https://go-review.googlesource.com/76470
Reviewed-by: Marvin Stenger <marvin.stenger94@gmail.com>
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
src/strings/strings.go
src/strings/strings_test.go

index 73bba9278c1023baadbb4dc28fbf3cae7b5df6ad..8520f8a732d91ceabcb437129a79c4bd6804fabc 100644 (file)
@@ -571,7 +571,33 @@ func ToUpper(s string) string {
 }
 
 // ToLower returns a copy of the string s with all Unicode letters mapped to their lower case.
-func ToLower(s string) string { return Map(unicode.ToLower, s) }
+func ToLower(s string) string {
+       isASCII, hasUpper := true, false
+       for i := 0; i < len(s); i++ {
+               c := s[i]
+               if c >= utf8.RuneSelf {
+                       isASCII = false
+                       break
+               }
+               hasUpper = hasUpper || (c >= 'A' && c <= 'Z')
+       }
+
+       if isASCII { // optimize for ASCII-only strings.
+               if !hasUpper {
+                       return s
+               }
+               b := make([]byte, len(s))
+               for i := 0; i < len(s); i++ {
+                       c := s[i]
+                       if c >= 'A' && c <= 'Z' {
+                               c += 'a' - 'A'
+                       }
+                       b[i] = c
+               }
+               return string(b)
+       }
+       return Map(unicode.ToLower, s)
+}
 
 // ToTitle returns a copy of the string s with all Unicode letters mapped to their title case.
 func ToTitle(s string) string { return Map(unicode.ToTitle, s) }
index b185e7eec8700e08458550a22781cc7593324e87..289dd92d51ec19928f424260ff5e9f5a5856ede3 100644 (file)
@@ -532,6 +532,8 @@ var lowerTests = []StringTest{
        {"abc", "abc"},
        {"AbC123", "abc123"},
        {"azAZ09_", "azaz09_"},
+       {"longStrinGwitHmixofsmaLLandcAps", "longstringwithmixofsmallandcaps"},
+       {"LONG\u2C6FSTRING\u2C6FWITH\u2C6FNONASCII\u2C6FCHARS", "long\u0250string\u0250with\u0250nonascii\u0250chars"},
        {"\u2C6D\u2C6D\u2C6D\u2C6D\u2C6D", "\u0251\u0251\u0251\u0251\u0251"}, // shrinks one byte per char
 }
 
@@ -664,6 +666,19 @@ func BenchmarkToUpper(b *testing.B) {
        }
 }
 
+func BenchmarkToLower(b *testing.B) {
+       for _, tc := range lowerTests {
+               b.Run(tc.in, func(b *testing.B) {
+                       for i := 0; i < b.N; i++ {
+                               actual := ToLower(tc.in)
+                               if actual != tc.out {
+                                       b.Errorf("ToLower(%q) = %q; want %q", tc.in, actual, tc.out)
+                               }
+                       }
+               })
+       }
+}
+
 func BenchmarkMapNoChanges(b *testing.B) {
        identity := func(r rune) rune {
                return r