Use a lock and channel free parallelization for subseq scoring

This commit is contained in:
Kovid Goyal
2025-06-06 06:39:09 +05:30
parent 3feea5b279
commit 3059c87bd0
2 changed files with 42 additions and 12 deletions

View File

@@ -8,7 +8,6 @@ import (
"strings" "strings"
"github.com/kovidgoyal/kitty/tools/utils" "github.com/kovidgoyal/kitty/tools/utils"
"github.com/kovidgoyal/kitty/tools/utils/images"
) )
var _ = fmt.Print var _ = fmt.Print
@@ -202,10 +201,7 @@ func score_item(item string, idx int, needle []rune, opts *resolved_options_type
} }
func ScoreItems(query string, items []string, opts Options) []*Match { func ScoreItems(query string, items []string, opts Options) []*Match {
ctx := images.Context{}
ctx.SetNumberOfThreads(opts.NumberOfThreads)
ans := make([]*Match, len(items)) ans := make([]*Match, len(items))
results := make(chan *Match, len(items))
nr := []rune(strings.ToLower(query)) nr := []rune(strings.ToLower(query))
if opts.Level1 == "" { if opts.Level1 == "" {
opts.Level1 = LEVEL1 opts.Level1 = LEVEL1
@@ -219,15 +215,12 @@ func ScoreItems(query string, items []string, opts Options) []*Match {
ropts := resolved_options_type{ ropts := resolved_options_type{
level1: []rune(opts.Level1), level2: []rune(opts.Level2), level3: []rune(opts.Level3), level1: []rune(opts.Level1), level2: []rune(opts.Level2), level3: []rune(opts.Level3),
} }
ctx.Parallel(0, len(items), func(nums <-chan int) { utils.Run_in_parallel_over_range(opts.NumberOfThreads, func(start, limit int) error {
w := workspace_type{} w := workspace_type{}
for i := range nums { for i := start; i < limit; i++ {
results <- score_item(items[i], i, nr, &ropts, &w) ans[i] = score_item(items[i], i, nr, &ropts, &w)
} }
}) return nil
close(results) }, 0, len(items))
for x := range results {
ans[x.idx] = x
}
return ans return ans
} }

View File

@@ -12,6 +12,7 @@ import (
"slices" "slices"
"strconv" "strconv"
"strings" "strings"
"sync"
"golang.org/x/exp/constraints" "golang.org/x/exp/constraints"
) )
@@ -86,6 +87,42 @@ func Format_stacktrace_on_panic(r any) (text string, err error) {
return strings.TrimSpace(text), err return strings.TrimSpace(text), err
} }
// Run the specified function in parallel over chunks from the specified range.
// If the function panics, it is turned into a regular error.
func Run_in_parallel_over_range(num_procs int, f func(int, int) error, start, limit int) (err error) {
num_items := limit - start
if num_procs <= 0 {
num_procs = runtime.NumCPU()
}
num_procs = max(1, min(num_procs, num_items))
chunk_sz := max(1, num_items/num_procs)
var wg sync.WaitGroup
echan := make(chan error, num_procs)
for start < limit {
end := min(start+chunk_sz, limit)
wg.Add(1)
go func(start, end int) {
defer func() {
if r := recover(); r != nil {
stacktrace, e := Format_stacktrace_on_panic(r)
echan <- fmt.Errorf("%s\n\n%w", stacktrace, e)
}
wg.Done()
}()
if err := f(start, end); err != nil {
echan <- err
}
}(start, end)
start = end
}
wg.Wait()
close(echan)
for qerr := range echan {
return qerr
}
return
}
func Map[T any, O any](f func(x T) O, s []T) []O { func Map[T any, O any](f func(x T) O, s []T) []O {
ans := make([]O, 0, len(s)) ans := make([]O, 0, len(s))
for _, x := range s { for _, x := range s {