Skip to content

ADR 0125: A keystroke should not allocate to ask a question

Status

Accepted

Context

Every key press asks two questions before anything else happens: is this chord the first half of a two-chord sequence (keys::isPrefix), and what command is it bound to (keys::commandFor). Measured at 20,000 iterations:

ns per key press
isPrefix 4584
commandFor 537

isPrefix cost eight times the lookup it guards, to answer "no" for almost every key.

Decision

It built two QStringLists of every key.* setting in the config, then ran ase_keymap_canonical over each one and over all sixty rows of the built-in table — per key press, to find the handful of rows containing a >.

Two things were wasted. The built-in table never changes, so its sequences are now split once into a function-local static. And a setting without a > cannot be a sequence, so a strchr now decides that before any QString exists: a config with no sequence bindings — which is most of them — allocates nothing at all.

isPrefix fell to 212ns.

Consequences

The three sequence functions (isPrefix, commandForSequence, sequenceHints) now share one Sequence shape and one way of reaching it, instead of each re-deriving the split inline.

commandFor was left alone. Assembling its setting name on the stack with qsnprintf instead of QStringLiteral(...).arg(...) was tried and measured slower — 1030ns against 752 for the pair — so it was reverted. Qt's short-string paths beat vsnprintf here; the obvious optimisation was the wrong one.

This is not where typing time goes. At 13KB of C a keystroke is ~800us, so 4.4us is half a percent. It matters for a file with no grammar, where a keystroke is 8us and this was most of it, and it is worth having because the cost bought nothing at any size.

lookupStaysOffTheAllocator asserts isPrefix stays cheaper than commandFor. A ratio rather than a number of nanoseconds, because an absolute bound fails under a sanitizer — the first version of this test did exactly that, and its early return then leaked the config it had built.