Improve search performance by pre-calculating character classes

This simple optmization can give more than 15% performance boost
in some scenarios.
This commit is contained in:
Junegunn Choi 2024-04-13 16:11:18 +09:00
parent 5643a306bd
commit 7ce6452d83
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627
3 changed files with 28 additions and 21 deletions

View File

@ -153,6 +153,9 @@ var (
bonusBoundaryDelimiter int16 = bonusBoundary + 1 bonusBoundaryDelimiter int16 = bonusBoundary + 1
initialCharClass charClass = charWhite initialCharClass charClass = charWhite
// A minor optimization that can give 15%+ performance boost
asciiCharClasses [unicode.MaxASCII + 1]charClass
) )
type charClass int type charClass int
@ -187,6 +190,22 @@ func Init(scheme string) bool {
default: default:
return false 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 return true
} }
@ -214,21 +233,6 @@ func alloc32(offset int, slab *util.Slab, size int) (int, []int32) {
return offset, make([]int32, size) 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 { func charClassOfNonAscii(char rune) charClass {
if unicode.IsLower(char) { if unicode.IsLower(char) {
return charLower return charLower
@ -248,7 +252,7 @@ func charClassOfNonAscii(char rune) charClass {
func charClassOf(char rune) charClass { func charClassOf(char rune) charClass {
if char <= unicode.MaxASCII { if char <= unicode.MaxASCII {
return charClassOfAscii(char) return asciiCharClasses[char]
} }
return charClassOfNonAscii(char) return charClassOfNonAscii(char)
} }
@ -447,9 +451,10 @@ func FuzzyMatchV2(caseSensitive bool, normalize bool, forward bool, input *util.
for off, char := range Tsub { for off, char := range Tsub {
var class charClass var class charClass
if char <= unicode.MaxASCII { if char <= unicode.MaxASCII {
class = charClassOfAscii(char) class = asciiCharClasses[char]
if !caseSensitive && class == charUpper { if !caseSensitive && class == charUpper {
char += 32 char += 32
Tsub[off] = char
} }
} else { } else {
class = charClassOfNonAscii(char) class = charClassOfNonAscii(char)
@ -459,9 +464,9 @@ func FuzzyMatchV2(caseSensitive bool, normalize bool, forward bool, input *util.
if normalize { if normalize {
char = normalizeRune(char) char = normalizeRune(char)
} }
Tsub[off] = char
} }
Tsub[off] = char
bonus := bonusFor(prevClass, class) bonus := bonusFor(prevClass, class)
Bsub[off] = bonus Bsub[off] = bonus
prevClass = class prevClass = class

View File

@ -9,6 +9,10 @@ import (
"github.com/junegunn/fzf/src/util" "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) { 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) assertMatch2(t, fun, caseSensitive, false, forward, input, pattern, sidx, eidx, score)
} }

View File

@ -2259,9 +2259,7 @@ func postProcessOptions(opts *Options) {
theme.Spinner = boldify(theme.Spinner) theme.Spinner = boldify(theme.Spinner)
} }
if opts.Scheme != "default" { processScheme(opts)
processScheme(opts)
}
} }
func expectsArbitraryString(opt string) bool { func expectsArbitraryString(opt string) bool {