From 934f2ede0b5cab50d0f36268fba4bb45a3777f73 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 9 Nov 2023 20:27:19 +0530 Subject: [PATCH] Start work on simple benchmark tool --- tools/cmd/benchmark/main.go | 149 +++++++++++++++++++++++++++++++ tools/cmd/tool/main.go | 2 + tools/tui/loop/api.go | 6 +- tools/tui/loop/run.go | 2 +- tools/tui/loop/terminal-state.go | 8 +- 5 files changed, 159 insertions(+), 8 deletions(-) create mode 100644 tools/cmd/benchmark/main.go diff --git a/tools/cmd/benchmark/main.go b/tools/cmd/benchmark/main.go new file mode 100644 index 000000000..1c090bb47 --- /dev/null +++ b/tools/cmd/benchmark/main.go @@ -0,0 +1,149 @@ +// License: GPLv3 Copyright: 2023, Kovid Goyal, + +package benchmark + +import ( + "bytes" + "fmt" + "math/rand" + "strings" + "sync" + "time" + + "kitty/tools/cli" + "kitty/tools/tty" + "kitty/tools/tui/loop" + "kitty/tools/utils" +) + +var _ = fmt.Print + +const reset = "\x1b]\x1b\\\x1bc" + +type benchmark_options struct { + alternate_screen bool + repeat_count int +} + +func default_benchmark_options() benchmark_options { + return benchmark_options{alternate_screen: true, repeat_count: 10} +} + +func benchmark_data(data string, opts benchmark_options) (duration time.Duration, err error) { + term, err := tty.OpenControllingTerm(tty.SetRaw) + if err != nil { + return 0, err + } + defer term.RestoreAndClose() + state := loop.TerminalStateOptions{Alternate_screen: opts.alternate_screen} + if _, err = term.WriteString(state.SetStateEscapeCodes()); err != nil { + return 0, err + } + defer func() { _, _ = term.WriteString(state.ResetStateEscapeCodes() + reset) }() + lock := sync.Mutex{} + const count = 3 + + go func() { + lock.Lock() + defer lock.Unlock() + buf := make([]byte, 8192) + var data []byte + q := []byte(strings.Repeat("\x1b[0n", count)) + for !bytes.Contains(data, q) { + n, err := term.Read(buf) + if err != nil { + break + } + data = append(data, buf[:n]...) + } + }() + + start := time.Now() + repeat_count := opts.repeat_count + for ; repeat_count > 0; repeat_count-- { + if _, err = term.WriteString(data); err != nil { + return 0, err + } + } + if _, err = term.WriteString(strings.Repeat("\x1b[5n", count)); err != nil { + return 0, err + } + lock.Lock() + duration = time.Since(start) / time.Duration(opts.repeat_count) + lock.Unlock() + return duration, nil +} + +func random_string_of_bytes(n int, alphabet string) string { + var src = rand.NewSource(time.Now().UnixNano()) + const ( + letterIdxBits = 7 // 7 bits to represent a letter index + letterIdxMask = 1<= 0; { + if remain == 0 { + cache, remain = src.Int63(), letterIdxMax + } + if idx := int(cache & letterIdxMask); idx < len(alphabet) { + b[i] = alphabet[idx] + i-- + } + cache >>= letterIdxBits + remain-- + } + return utils.UnsafeBytesToString(b) +} + +type result struct { + desc string + data_sz int + duration time.Duration +} + +func simple_ascii() (r result, err error) { + data := random_string_of_bytes(1024*1024+13, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ \n\t\r `~!@#$%^&*()_+-=[]{}\\|;:'\",<.>/?") + duration, err := benchmark_data(data, default_benchmark_options()) + if err != nil { + return result{}, err + } + return result{"Simple ascii characters", len(data), duration}, nil +} + +func present_result(r result) { + rate := float64(r.data_sz) / float64(r.duration) + rate *= 1e3 + fmt.Println("\t"+r.desc+":", r.duration, fmt.Sprintf("%.2f", rate), "GiB/s") +} + +func main() (err error) { + var results []result + var r result + if r, err = simple_ascii(); err != nil { + return err + } + results = append(results, r) + + fmt.Println(reset + "Results:") + for _, r := range results { + present_result(r) + } + return +} + +func EntryPoint(root *cli.Command) { + sc := root.AddSubCommand(&cli.Command{ + Name: "__benchmark__", + ShortDescription: "Run various benchmarks", + Hidden: true, + Run: func(cmd *cli.Command, args []string) (ret int, err error) { + if err = main(); err != nil { + ret = 1 + } + return + }, + }) + _ = sc +} diff --git a/tools/cmd/tool/main.go b/tools/cmd/tool/main.go index c8e328be7..51a25b026 100644 --- a/tools/cmd/tool/main.go +++ b/tools/cmd/tool/main.go @@ -18,6 +18,7 @@ import ( "kitty/kittens/unicode_input" "kitty/tools/cli" "kitty/tools/cmd/at" + "kitty/tools/cmd/benchmark" "kitty/tools/cmd/edit_in_kitty" "kitty/tools/cmd/mouse_demo" "kitty/tools/cmd/pytest" @@ -121,4 +122,5 @@ func KittyToolEntryPoints(root *cli.Command) { return }, }) + benchmark.EntryPoint(root) } diff --git a/tools/tui/loop/api.go b/tools/tui/loop/api.go index 31e896d11..dbd7da9cd 100644 --- a/tools/tui/loop/api.go +++ b/tools/tui/loop/api.go @@ -129,12 +129,12 @@ func (self *Loop) RemoveTimer(id IdType) bool { } func (self *Loop) NoAlternateScreen() *Loop { - self.terminal_options.alternate_screen = false + self.terminal_options.Alternate_screen = false return self } func NoAlternateScreen(self *Loop) { - self.terminal_options.alternate_screen = false + self.terminal_options.Alternate_screen = false } func (self *Loop) OnlyDisambiguateKeys() *Loop { @@ -268,7 +268,7 @@ func (self *Loop) Run() (err error) { } fmt.Fprintf(os.Stderr, "%s\r\n\t%s:%d\r\n", frame.Function, frame.File, frame.Line) } - if self.terminal_options.alternate_screen { + if self.terminal_options.Alternate_screen { term, err := tty.OpenControllingTerm(tty.SetRaw) if err == nil { defer term.RestoreAndClose() diff --git a/tools/tui/loop/run.go b/tools/tui/loop/run.go index 1733f5623..a0cda20c4 100644 --- a/tools/tui/loop/run.go +++ b/tools/tui/loop/run.go @@ -22,7 +22,7 @@ var SIGNULL unix.Signal func new_loop() *Loop { l := Loop{controlling_term: nil} - l.terminal_options.alternate_screen = true + l.terminal_options.Alternate_screen = true l.terminal_options.restore_colors = true l.terminal_options.kitty_keyboard_mode = DISAMBIGUATE_KEYS | REPORT_ALTERNATE_KEYS | REPORT_ALL_KEYS_AS_ESCAPE_CODES | REPORT_TEXT_WITH_KEYS l.escape_code_parser.HandleCSI = l.handle_csi diff --git a/tools/tui/loop/terminal-state.go b/tools/tui/loop/terminal-state.go index 645105854..778adfdc3 100644 --- a/tools/tui/loop/terminal-state.go +++ b/tools/tui/loop/terminal-state.go @@ -94,7 +94,7 @@ const ( ) type TerminalStateOptions struct { - alternate_screen, restore_colors bool + Alternate_screen, restore_colors bool mouse_tracking MouseTracking kitty_keyboard_mode KeyboardStateBits } @@ -114,7 +114,7 @@ func reset_modes(sb *strings.Builder, modes ...Mode) { func (self *TerminalStateOptions) SetStateEscapeCodes() string { var sb strings.Builder sb.Grow(256) - if self.alternate_screen { + if self.Alternate_screen { sb.WriteString(SAVE_CURSOR) } sb.WriteString(SAVE_PRIVATE_MODE_VALUES) @@ -126,7 +126,7 @@ func (self *TerminalStateOptions) SetStateEscapeCodes() string { IRM, DECKM, DECSCNM, BRACKETED_PASTE, FOCUS_TRACKING, MOUSE_BUTTON_TRACKING, MOUSE_MOTION_TRACKING, MOUSE_MOVE_TRACKING, MOUSE_UTF8_MODE, MOUSE_SGR_MODE) set_modes(&sb, DECARM, DECAWM, DECTCEM) - if self.alternate_screen { + if self.Alternate_screen { set_modes(&sb, ALTERNATE_SCREEN) sb.WriteString(CLEAR_SCREEN) } @@ -153,7 +153,7 @@ func (self *TerminalStateOptions) ResetStateEscapeCodes() string { var sb strings.Builder sb.Grow(64) sb.WriteString("\033[