Speed up CSI escape code parsing by ~30%

This commit is contained in:
Kovid Goyal 2026-08-16 22:08:28 +05:30
parent 07ea7583ac
commit 82ae858a03
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
2 changed files with 25 additions and 5 deletions

View file

@ -190,6 +190,8 @@ Detailed list of changes
- Speed up SIMD UTF-8 decoding: ~20% faster for ASCII and ~50% faster for multi-byte text on both x86 and ARM, by processing pairs of vectors of plain ASCII text at a time and compacting decoded codepoints within 128-bit lanes, avoiding expensive cross-lane operations. Also add an AVX-512 based decoder, another ~75% faster for multi-byte text, used automatically on CPUs that support it (Intel Ice Lake+, AMD Zen 4+). Additionally fix the SIMD decoders failing to emit U+FFFD for an incomplete UTF-8 sequence cut off by an escape code
- Improve throughput when processing escape code heavy input by ~30% by taking the input buffer lock once per buffer of input rather than once per escape code and using cheaper arithmetic to parse CSI parameters
- The :opt:`scrollbar` option now takes a new value ``scrolled-or-hovered`` to also show the scrollbar when the mouse moves over the scrollbar region (:pull:`10345`)
- A new :code:`kitten @ screenshot` remote control command to take a pixel perfect PNG screenshot of an OS Window, tab or window

View file

@ -842,17 +842,28 @@ commit_csi_param(PS *self UNUSED, ParsedCSI *csi) {
REPORT_ERROR("CSI escape code has too many parameters, ignoring it");
return false;
}
csi->params[csi->num_params++] = csi->mult * (csi->accumulator / digit_multipliers[csi->num_digits - 1]);
csi->params[csi->num_params++] = csi->mult * (int64_t)csi->accumulator;
csi->num_digits = 0;
csi->mult = 1;
csi->accumulator = 0;
return true;
}
#define MAX_CSI_DIGITS 16u
static void
csi_add_digit(ParsedCSI *csi, uint8_t ch) {
if (UNLIKELY(csi->num_digits >= arraysz(digit_multipliers))) return;
csi->accumulator += (ch - '0') * digit_multipliers[csi->num_digits++];
if (UNLIKELY(csi->num_digits >= MAX_CSI_DIGITS)) return;
csi->num_digits++;
csi->accumulator = csi->accumulator * 10 + (ch - '0');
}
static void
consume_csi_digit_run(ParsedCSI *csi, const uint8_t *buf, size_t *pos, const size_t sz) {
// consume a run of parameter digits without re-entering the per-byte state machine
size_t p = *pos;
while (p < sz && (uint8_t)(buf[p] - '0') <= 9) csi_add_digit(csi, buf[p++]);
*pos = p;
}
static bool
@ -870,6 +881,7 @@ csi_parse_loop(PS *self, ParsedCSI *csi, const uint8_t *buf, size_t *pos, const
break;
case DIGIT:
csi_add_digit(csi, ch);
consume_csi_digit_run(csi, buf, pos, sz);
csi->state = CSI_BODY;
break;
case '?':
@ -937,7 +949,10 @@ csi_parse_loop(PS *self, ParsedCSI *csi, const uint8_t *buf, size_t *pos, const
if (!commit_csi_param(self, csi)) return true;
csi->is_sub_param[csi->num_params] = false;
break;
case DIGIT: csi_add_digit(csi, ch); break;
case DIGIT:
csi_add_digit(csi, ch);
consume_csi_digit_run(csi, buf, pos, sz);
break;
default: REPORT_ERROR("Invalid character in CSI: %s (0x%x), ignoring the sequence", csi_letter(ch), ch); return true;
}
break;
@ -1594,7 +1609,10 @@ run_worker(void *p, ParseData *pd, bool flush) {
self->read.consumed = 0;
do {
end_with_lock;
{ consume_input(self, pd->dump_callback, screen->window_id); }
// consume all already read input without lock cycling, the
// writer thread only modifies write.pending and buffer space
// beyond read.sz + write.pending
do { consume_input(self, pd->dump_callback, screen->window_id); } while (self->read.pos < self->read.sz);
with_lock;
self->read.sz += self->write.pending;
self->write.pending = 0;