Skip to content

Commit

Permalink
perf: avoid allocs in unicodeFoldTransformer
Browse files Browse the repository at this point in the history
There is no need for temporary slices;
the transformation can happen rune-by-rune.

goos: darwin
goarch: arm64
pkg: github.com/lithammer/fuzzysearch/fuzzy
                              │      a       │                  b                  │
                              │    sec/op    │   sec/op     vs base                │
Match-8                          16.06n ± 3%   16.04n ± 3%        ~ (p=0.447 n=10)
MatchBigLate-8                   1.008µ ± 0%   1.007µ ± 1%        ~ (p=1.000 n=10)
MatchBigEarly-8                  12.56n ± 2%   12.58n ± 3%        ~ (p=0.303 n=10)
MatchFold-8                      334.5n ± 1%   137.9n ± 2%  -58.77% (p=0.000 n=10)
MatchFoldBigLate-8              27.349µ ± 1%   7.088µ ± 1%  -74.08% (p=0.000 n=10)
MatchFoldBigEarly-8             26.390µ ± 2%   6.091µ ± 9%  -76.92% (p=0.000 n=10)
RankMatch-8                      17.66n ± 1%   17.59n ± 0%   -0.40% (p=0.034 n=10)
RankMatchBigLate-8               1.010µ ± 1%   1.008µ ± 3%        ~ (p=0.368 n=10)
RankMatchBigEarly-8              1.236µ ± 0%   1.235µ ± 2%        ~ (p=0.667 n=10)
LevenshteinDistance-8            55.65n ± 3%   55.52n ± 3%        ~ (p=0.054 n=10)
LevenshteinDistanceBigLate-8     20.53µ ± 1%   20.47µ ± 3%   -0.29% (p=0.042 n=10)
LevenshteinDistanceBigEarly-8    20.57µ ± 3%   20.47µ ± 3%        ~ (p=0.060 n=10)
geomean                          736.4n        540.1n       -26.66%

                              │        a        │                   b                    │
                              │      B/op       │     B/op      vs base                  │
Match-8                            0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=10) ¹
MatchBigLate-8                     0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=10) ¹
MatchBigEarly-8                    0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=10) ¹
MatchFold-8                        720.0 ± 0%       512.0 ± 0%  -28.89% (p=0.000 n=10)
MatchFoldBigLate-8              36.711Ki ± 0%     8.750Ki ± 0%  -76.17% (p=0.000 n=10)
MatchFoldBigEarly-8             36.711Ki ± 0%     8.750Ki ± 0%  -76.17% (p=0.000 n=10)
RankMatch-8                        0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=10) ¹
RankMatchBigLate-8                 0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=10) ¹
RankMatchBigEarly-8                0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=10) ¹
LevenshteinDistance-8              0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=10) ¹
LevenshteinDistanceBigLate-8     6.375Ki ± 0%     6.375Ki ± 0%        ~ (p=1.000 n=10) ¹
LevenshteinDistanceBigEarly-8    6.375Ki ± 0%     6.375Ki ± 0%        ~ (p=1.000 n=10) ¹
geomean                                       ²                 -23.46%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                              │       a       │                  b                   │
                              │   allocs/op   │ allocs/op   vs base                  │
Match-8                          0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=10) ¹
MatchBigLate-8                   0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=10) ¹
MatchBigEarly-8                  0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=10) ¹
MatchFold-8                     10.000 ± 0%     2.000 ± 0%  -80.00% (p=0.000 n=10)
MatchFoldBigLate-8              186.00 ± 0%     25.00 ± 0%  -86.56% (p=0.000 n=10)
MatchFoldBigEarly-8             186.00 ± 0%     25.00 ± 0%  -86.56% (p=0.000 n=10)
RankMatch-8                      0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=10) ¹
RankMatchBigLate-8               0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=10) ¹
RankMatchBigEarly-8              0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=10) ¹
LevenshteinDistance-8            0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=10) ¹
LevenshteinDistanceBigLate-8     1.000 ± 0%     1.000 ± 0%        ~ (p=1.000 n=10) ¹
LevenshteinDistanceBigEarly-8    1.000 ± 0%     1.000 ± 0%        ~ (p=1.000 n=10) ¹
geomean                                     ²               -37.41%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean
  • Loading branch information
josharian committed May 2, 2023
1 parent 1174331 commit 1faf692
Showing 1 changed file with 14 additions and 12 deletions.
26 changes: 14 additions & 12 deletions fuzzy/fuzzy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
package fuzzy

import (
"bytes"
"unicode"
"unicode/utf8"

Expand Down Expand Up @@ -251,18 +250,21 @@ func stringTransform(s string, t transform.Transformer) (transformed string) {
type unicodeFoldTransformer struct{ transform.NopResetter }

func (unicodeFoldTransformer) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
runes := bytes.Runes(src)
var lowerRunes []rune
for _, r := range runes {
lowerRunes = append(lowerRunes, unicode.ToLower(r))
}

srcBytes := []byte(string(lowerRunes))
n := copy(dst, srcBytes)
if n < len(srcBytes) {
err = transform.ErrShortDst
n := 0
// Converting src to a string allocates.
// In theory, it need not; see https://go.dev/issue/27148.
// It is possible to write this loop using utf8.DecodeRune
// and thereby avoid allocations, but it is noticeably slower.
// So just let's wait for the compiler to get smarter.
for _, r := range string(src) {
r = unicode.ToLower(r)
x := utf8.RuneLen(r)
if x > len(dst[n:]) {
err = transform.ErrShortDst
break
}
n += utf8.EncodeRune(dst[n:], r)
}

return n, n, err
}

Expand Down

0 comments on commit 1faf692

Please sign in to comment.