From 7ce6452d83a62a3bc409fef8face1cfaef6e0146 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Sat, 13 Apr 2024 16:11:18 +0900 Subject: [PATCH] Improve search performance by pre-calculating character classes This simple optmization can give more than 15% performance boost in some scenarios. --- src/algo/algo.go | 41 +++++++++++++++++++++++------------------ src/algo/algo_test.go | 4 ++++ src/options.go | 4 +--- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/algo/algo.go b/src/algo/algo.go index 5cb82bdd..6e0d437c 100644 --- a/src/algo/algo.go +++ b/src/algo/algo.go @@ -153,6 +153,9 @@ var ( bonusBoundaryDelimiter int16 = bonusBoundary + 1 initialCharClass charClass = charWhite + + // A minor optimization that can give 15%+ performance boost + asciiCharClasses [unicode.MaxASCII + 1]charClass ) type charClass int @@ -187,6 +190,22 @@ func Init(scheme string) bool { default: return false } + for i := 0; i <= unicode.MaxASCII; i++ { + char := rune(i) + c := charNonWord + if char >= 'a' && char <= 'z' { + c = charLower + } else if char >= 'A' && char <= 'Z' { + c = charUpper + } else if char >= '0' && char <= '9' { + c = charNumber + } else if strings.ContainsRune(whiteChars, char) { + c = charWhite + } else if strings.ContainsRune(delimiterChars, char) { + c = charDelimiter + } + asciiCharClasses[i] = c + } return true } @@ -214,21 +233,6 @@ func alloc32(offset int, slab *util.Slab, size int) (int, []int32) { return offset, make([]int32, size) } -func charClassOfAscii(char rune) charClass { - if char >= 'a' && char <= 'z' { - return charLower - } else if char >= 'A' && char <= 'Z' { - return charUpper - } else if char >= '0' && char <= '9' { - return charNumber - } else if strings.ContainsRune(whiteChars, char) { - return charWhite - } else if strings.ContainsRune(delimiterChars, char) { - return charDelimiter - } - return charNonWord -} - func charClassOfNonAscii(char rune) charClass { if unicode.IsLower(char) { return charLower @@ -248,7 +252,7 @@ func charClassOfNonAscii(char rune) charClass { func charClassOf(char rune) charClass { if char <= unicode.MaxASCII { - return charClassOfAscii(char) + return asciiCharClasses[char] } return charClassOfNonAscii(char) } @@ -447,9 +451,10 @@ func FuzzyMatchV2(caseSensitive bool, normalize bool, forward bool, input *util. for off, char := range Tsub { var class charClass if char <= unicode.MaxASCII { - class = charClassOfAscii(char) + class = asciiCharClasses[char] if !caseSensitive && class == charUpper { char += 32 + Tsub[off] = char } } else { class = charClassOfNonAscii(char) @@ -459,9 +464,9 @@ func FuzzyMatchV2(caseSensitive bool, normalize bool, forward bool, input *util. if normalize { char = normalizeRune(char) } + Tsub[off] = char } - Tsub[off] = char bonus := bonusFor(prevClass, class) Bsub[off] = bonus prevClass = class diff --git a/src/algo/algo_test.go b/src/algo/algo_test.go index a7c4e1d3..b5ed0e77 100644 --- a/src/algo/algo_test.go +++ b/src/algo/algo_test.go @@ -9,6 +9,10 @@ import ( "github.com/junegunn/fzf/src/util" ) +func init() { + Init("default") +} + func assertMatch(t *testing.T, fun Algo, caseSensitive, forward bool, input, pattern string, sidx int, eidx int, score int) { assertMatch2(t, fun, caseSensitive, false, forward, input, pattern, sidx, eidx, score) } diff --git a/src/options.go b/src/options.go index 76fe9ee9..38a5ba7c 100644 --- a/src/options.go +++ b/src/options.go @@ -2259,9 +2259,7 @@ func postProcessOptions(opts *Options) { theme.Spinner = boldify(theme.Spinner) } - if opts.Scheme != "default" { - processScheme(opts) - } + processScheme(opts) } func expectsArbitraryString(opt string) bool {