Skip to content

Commit da5f36d

Browse files
committed
[tailscale1.17] bytes, strings: cache cutsetFuncs in Trim
[out for review upstream: https://go-review.googlesource.com/c/go/+/271669] This avoids allocations that occur on every call to Trim. Typical usage of Trim involves using the same Trim set every time. Package bytes benchmarks: name old time/op new time/op delta TrimASCII/1:1-8 47.8ns ± 3% 55.8ns ± 2% +16.72% (p=0.000 n=19+18) TrimASCII/1:2-8 76.7ns ± 2% 57.3ns ± 1% -25.25% (p=0.000 n=20+19) TrimASCII/1:4-8 79.5ns ± 2% 53.8ns ± 1% -32.31% (p=0.000 n=20+18) TrimASCII/1:8-8 85.1ns ± 2% 54.1ns ± 2% -36.38% (p=0.000 n=20+19) TrimASCII/1:16-8 95.9ns ± 2% 54.1ns ± 1% -43.55% (p=0.000 n=19+19) TrimASCII/16:1-8 104ns ± 1% 102ns ± 1% -1.72% (p=0.000 n=19+19) TrimASCII/16:2-8 123ns ± 2% 102ns ± 1% -17.47% (p=0.000 n=19+20) TrimASCII/16:4-8 127ns ± 2% 100ns ± 1% -21.35% (p=0.000 n=19+20) TrimASCII/16:8-8 132ns ± 2% 103ns ± 1% -22.23% (p=0.000 n=20+17) TrimASCII/16:16-8 143ns ± 1% 101ns ± 2% -29.73% (p=0.000 n=20+17) TrimASCII/256:1-8 834ns ± 1% 829ns ± 1% -0.56% (p=0.003 n=19+17) TrimASCII/256:2-8 878ns ± 1% 848ns ± 1% -3.48% (p=0.000 n=19+20) TrimASCII/256:4-8 881ns ± 1% 848ns ± 1% -3.68% (p=0.000 n=20+17) TrimASCII/256:8-8 887ns ± 1% 847ns ± 1% -4.52% (p=0.000 n=19+19) TrimASCII/256:16-8 898ns ± 1% 849ns ± 3% -5.38% (p=0.000 n=18+20) TrimASCII/4096:1-8 12.4µs ± 1% 12.3µs ± 2% -1.26% (p=0.000 n=20+19) TrimASCII/4096:2-8 12.9µs ± 1% 12.8µs ± 1% -0.69% (p=0.007 n=20+20) TrimASCII/4096:4-8 12.8µs ± 0% 12.8µs ± 2% ~ (p=0.296 n=19+17) TrimASCII/4096:8-8 12.8µs ± 2% 12.8µs ± 1% ~ (p=0.149 n=18+18) TrimASCII/4096:16-8 12.9µs ± 1% 12.8µs ± 2% -0.65% (p=0.012 n=18+20) name old alloc/op new alloc/op delta TrimASCII/1:1-8 24.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/1:2-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/1:4-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/1:8-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/1:16-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/16:1-8 24.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/16:2-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/16:4-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/16:8-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/16:16-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/256:1-8 24.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/256:2-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/256:4-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/256:8-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/256:16-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/4096:1-8 24.0B ± 0% 0.0B -100.00% (p=0.000 n=20+19) TrimASCII/4096:2-8 49.0B ± 0% 1.0B ± 0% -97.96% (p=0.000 n=20+20) TrimASCII/4096:4-8 51.0B ± 0% 3.0B ± 0% -94.12% (p=0.000 n=20+18) TrimASCII/4096:8-8 54.4B ± 1% 6.2B ±12% -88.50% (p=0.000 n=20+20) TrimASCII/4096:16-8 61.6B ± 2% 13.4B ±12% -78.23% (p=0.000 n=20+20) name old allocs/op new allocs/op delta TrimASCII/1:1-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/1:2-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/1:4-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/1:8-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/1:16-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/16:1-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/16:2-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/16:4-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/16:8-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/16:16-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/256:1-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/256:2-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/256:4-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/256:8-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/256:16-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/4096:1-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/4096:2-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/4096:4-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/4096:8-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/4096:16-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) Package strings benchmarks: name old time/op new time/op delta TrimASCII/1:1-8 47.7ns ± 4% 55.2ns ± 1% +15.77% (p=0.000 n=18+17) TrimASCII/1:2-8 74.4ns ± 3% 55.0ns ± 1% -26.01% (p=0.000 n=19+17) TrimASCII/1:4-8 76.8ns ± 1% 54.8ns ± 1% -28.68% (p=0.000 n=16+16) TrimASCII/1:8-8 82.3ns ± 2% 54.9ns ± 1% -33.37% (p=0.000 n=18+18) TrimASCII/1:16-8 93.2ns ± 1% 51.4ns ± 1% -44.89% (p=0.000 n=18+20) TrimASCII/16:1-8 99.5ns ± 2% 95.0ns ± 1% -4.51% (p=0.000 n=20+17) TrimASCII/16:2-8 116ns ± 2% 98ns ± 3% -16.23% (p=0.000 n=20+18) TrimASCII/16:4-8 119ns ± 1% 97ns ± 1% -18.47% (p=0.000 n=19+19) TrimASCII/16:8-8 124ns ± 1% 97ns ± 1% -22.24% (p=0.000 n=19+16) TrimASCII/16:16-8 136ns ± 2% 93ns ± 1% -31.49% (p=0.000 n=20+17) TrimASCII/256:1-8 752ns ± 0% 748ns ± 2% -0.52% (p=0.003 n=19+19) TrimASCII/256:2-8 783ns ± 1% 759ns ± 1% -3.07% (p=0.000 n=19+20) TrimASCII/256:4-8 788ns ± 1% 757ns ± 1% -3.98% (p=0.000 n=19+17) TrimASCII/256:8-8 793ns ± 1% 758ns ± 1% -4.40% (p=0.000 n=19+19) TrimASCII/256:16-8 803ns ± 1% 751ns ± 2% -6.42% (p=0.000 n=20+20) TrimASCII/4096:1-8 11.1µs ± 1% 11.0µs ± 2% -1.43% (p=0.000 n=20+18) TrimASCII/4096:2-8 11.3µs ± 1% 11.1µs ± 1% -1.30% (p=0.000 n=20+19) TrimASCII/4096:4-8 11.3µs ± 2% 11.2µs ± 1% -1.27% (p=0.000 n=19+20) TrimASCII/4096:8-8 11.4µs ± 1% 11.1µs ± 1% -2.04% (p=0.000 n=19+20) TrimASCII/4096:16-8 11.4µs ± 4% 11.2µs ± 2% -2.02% (p=0.000 n=17+20) name old alloc/op new alloc/op delta TrimASCII/1:1-8 24.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/1:2-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/1:4-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/1:8-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/1:16-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/16:1-8 24.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/16:2-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/16:4-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/16:8-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/16:16-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/256:1-8 24.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/256:2-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/256:4-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/256:8-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/256:16-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/4096:1-8 24.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) TrimASCII/4096:2-8 49.0B ± 0% 1.0B ± 0% -97.96% (p=0.000 n=20+20) TrimASCII/4096:4-8 51.0B ± 0% 3.0B ± 0% -94.12% (p=0.000 n=20+17) TrimASCII/4096:8-8 54.0B ± 0% 6.0B ± 0% -88.89% (p=0.000 n=20+16) TrimASCII/4096:16-8 60.0B ± 0% 11.5B ±13% -80.79% (p=0.000 n=18+19) name old allocs/op new allocs/op delta TrimASCII/1:1-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/1:2-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/1:4-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/1:8-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/1:16-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/16:1-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/16:2-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/16:4-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/16:8-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/16:16-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/256:1-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/256:2-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/256:4-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/256:8-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/256:16-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/4096:1-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/4096:2-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/4096:4-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/4096:8-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) TrimASCII/4096:16-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) Change-Id: I4465cc4ae5561126d1e2e9e7217749ac42516676 (cherry picked from commit 5bb8bf3)
1 parent 30d8f03 commit da5f36d

File tree

2 files changed

+33
-15
lines changed

2 files changed

+33
-15
lines changed

src/bytes/bytes.go

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package bytes
88

99
import (
1010
"internal/bytealg"
11+
"sync"
1112
"unicode"
1213
"unicode/utf8"
1314
)
@@ -887,25 +888,33 @@ func (as *asciiSet) contains(c byte) bool {
887888
return (as[c>>5] & (1 << uint(c&31))) != 0
888889
}
889890

891+
var cutsetFuncCache sync.Map // of string -> func(rune) bool
892+
890893
func makeCutsetFunc(cutset string) func(r rune) bool {
894+
if fn, ok := cutsetFuncCache.Load(cutset); ok {
895+
return fn.(func(rune) bool)
896+
}
897+
var fn func(rune) bool
891898
if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
892-
return func(r rune) bool {
899+
fn = func(r rune) bool {
893900
return r == rune(cutset[0])
894901
}
895-
}
896-
if as, isASCII := makeASCIISet(cutset); isASCII {
897-
return func(r rune) bool {
902+
} else if as, isASCII := makeASCIISet(cutset); isASCII {
903+
fn = func(r rune) bool {
898904
return r < utf8.RuneSelf && as.contains(byte(r))
899905
}
900-
}
901-
return func(r rune) bool {
902-
for _, c := range cutset {
903-
if c == r {
904-
return true
906+
} else {
907+
fn = func(r rune) bool {
908+
for _, c := range cutset {
909+
if c == r {
910+
return true
911+
}
905912
}
913+
return false
906914
}
907-
return false
908915
}
916+
cutsetFuncCache.LoadOrStore(cutset, fn)
917+
return fn
909918
}
910919

911920
// Trim returns a subslice of s by slicing off all leading and

src/strings/strings.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package strings
99

1010
import (
1111
"internal/bytealg"
12+
"sync"
1213
"unicode"
1314
"unicode/utf8"
1415
)
@@ -817,18 +818,26 @@ func (as *asciiSet) contains(c byte) bool {
817818
return (as[c>>5] & (1 << uint(c&31))) != 0
818819
}
819820

821+
var cutsetFuncCache sync.Map // of string -> func(rune) bool
822+
820823
func makeCutsetFunc(cutset string) func(rune) bool {
824+
if fn, ok := cutsetFuncCache.Load(cutset); ok {
825+
return fn.(func(rune) bool)
826+
}
827+
var fn func(rune) bool
821828
if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
822-
return func(r rune) bool {
829+
fn = func(r rune) bool {
823830
return r == rune(cutset[0])
824831
}
825-
}
826-
if as, isASCII := makeASCIISet(cutset); isASCII {
827-
return func(r rune) bool {
832+
} else if as, isASCII := makeASCIISet(cutset); isASCII {
833+
fn = func(r rune) bool {
828834
return r < utf8.RuneSelf && as.contains(byte(r))
829835
}
836+
} else {
837+
fn = func(r rune) bool { return IndexRune(cutset, r) >= 0 }
830838
}
831-
return func(r rune) bool { return IndexRune(cutset, r) >= 0 }
839+
cutsetFuncCache.LoadOrStore(cutset, fn)
840+
return fn
832841
}
833842

834843
// Trim returns a slice of the string s with all leading and

0 commit comments

Comments
 (0)