myelinContext-bound keymaps and which-key menus for Go programs

☰ Contents · Reference › Performance

Performance

A key no binding starts with costs one map lookup. Matching a key against bindings with contexts takes under half a microsecond and allocates nothing, and compiling a context, once when the keymap loads, takes about two microseconds. A program can look a key up on every keystroke, and draw a menu on every frame, without counting.

The numbers

Operation Time Allocations
A key no binding starts with (Feed) 45 ns 1
A key matched against bindings with contexts (Match) 0.44 µs 0
The leader's menu (Menu) 17 µs 60
A palette row's key (KeyFor) 0.41 µs 0
Compiling a context, once, at load (Compile) 2 µs 22

A query evaluates each distinct context once, on an activation the keymap pools, and evaluating a context doesn't allocate, so a match allocates nothing. A key no binding starts with allocates only the slice that hands it back to the program. A menu's allocations are its items.

How they were measured

The keymap is the size of a program's defaults: a hundred bindings. For each of 25 keys there is a binding under the leader key, a sequence that continues it, a binding in the context sidebar && panel.ext == 'git' && !input, and one in buffer.tags['format'] == 'gofmt', with a group labelling each leader prefix. The path is a git panel in a window's sidebar, three nodes deep.

The times are the medians of six runs of go test -bench . -benchmem, with Go 1.26 on linux/amd64, on a laptop with an AMD Ryzen AI 9 HX 370 that was doing other work. They move with the machine and its load; the allocations don't.

Size

myelin requires cel-lite, its own key names and msgp's runtime, which need only Go's standard library. A small program that declares a schema, builds a keymap with contexts and matches a key is 2.5 MB built for linux/amd64, and 4.2 MB for js/wasm, with -trimpath -ldflags=-s and Go 1.26. A program that prints hello is 1.6 MB, or 2.6 MB in js/wasm, so most of that is Go's own runtime.

What the gate holds

A test fails when an operation allocates more than it does now:

  • a key no binding starts with: 1;
  • a prefix of a longer binding, a match in contexts and a palette row's key: none;
  • the leader's menu: at most 100;
  • every key's binding on a path six nodes deep (Effective): at most 8.

The times aren't checked, since they move with the machine.

Running them

mise exec -- make bench

runs every benchmark, myelinkey's included, with their allocations. To compare two runs, save each one's output and give both to benchstat.

Starting the terminal…