diff --git a/src/core.go b/src/core.go index 7b980ccf..6c453082 100644 --- a/src/core.go +++ b/src/core.go @@ -2,6 +2,7 @@ package fzf import ( + "fmt" "maps" "os" "sync" @@ -181,7 +182,7 @@ func Run(opts *Options) (int, error) { } // Reader - streamingFilter := opts.Filter != nil && !sort && !opts.Tac && !opts.Sync + streamingFilter := opts.Filter != nil && !sort && !opts.Tac && !opts.Sync && opts.Bench == 0 var reader *Reader if !streamingFilter { reader = NewReader(func(data []byte) bool { @@ -274,6 +275,37 @@ func Run(opts *Options) (int, error) { // NOTE: Streaming filter is inherently not compatible with --tail snapshot, _, _ := chunkList.Snapshot(opts.Tail) + + if opts.Bench > 0 { + // Benchmark mode: repeat scan for the given duration + var times []time.Duration + deadline := time.Now().Add(opts.Bench) + for time.Now().Before(deadline) { + cache.Clear() + start := time.Now() + matcher.scan(MatchRequest{ + chunks: snapshot, + pattern: pattern}) + times = append(times, time.Since(start)) + } + // Print stats + var total time.Duration + minD, maxD := times[0], times[0] + for _, d := range times { + total += d + if d < minD { + minD = d + } + if d > maxD { + maxD = d + } + } + avg := total / time.Duration(len(times)) + fmt.Printf(" %d iterations avg: %v min: %v max: %v total: %v\n", + len(times), avg, minD, maxD, total) + return ExitOk, nil + } + result := matcher.scan(MatchRequest{ chunks: snapshot, pattern: pattern}) diff --git a/src/options.go b/src/options.go index 47b0095a..fc055968 100644 --- a/src/options.go +++ b/src/options.go @@ -8,6 +8,7 @@ import ( "regexp" "strconv" "strings" + "time" "unicode" "github.com/junegunn/fzf/src/algo" @@ -677,6 +678,7 @@ type Options struct { WalkerSkip []string Version bool Help bool + Bench time.Duration CPUProfile string MEMProfile string BlockProfile string @@ -3373,6 +3375,16 @@ func parseOptions(index *int, opts *Options, allArgs []string) error { return err } opts.WalkerSkip = filterNonEmpty(strings.Split(str, ",")) + case "--bench": + str, err := nextString("duration required (e.g. 3s, 500ms)") + if err != nil { + return err + } + dur, err := time.ParseDuration(str) + if err != nil { + return errors.New("invalid duration for --bench: " + str) + } + opts.Bench = dur case "--profile-cpu": if opts.CPUProfile, err = nextString("file path required: cpu"); err != nil { return err