process ASCII events manually to avoid function call overhead

This commit is contained in:
Mitchell Hashimoto
2022-09-01 17:53:40 -07:00
parent d404be2993
commit 30a14d230e
5 changed files with 53 additions and 4 deletions

View File

@ -1271,9 +1271,43 @@ fn ttyRead(t: *libuv.Tty, n: isize, buf: []const u8) void {
// Schedule a render
win.render_timer.schedule() catch unreachable;
// Process the terminal data
win.terminal_stream.nextSlice(buf[0..@intCast(usize, n)]) catch |err|
log.err("error processing terminal data: {}", .{err});
// Process the terminal data. This is an extremely hot part of the
// terminal emulator, so we do some abstraction leakage to avoid
// function calls and unnecessary logic.
//
// The ground state is the only state that we can see and print/execute
// ASCII, so we only execute this hot path if we're already in the ground
// state.
//
// Empirically, this alone improved throughput of large text output by ~20%.
var i: usize = 0;
const end = @intCast(usize, n);
if (win.terminal_stream.parser.state == .ground) {
for (buf[i..end]) |c| {
switch (terminal.parse_table.table[c][@enumToInt(terminal.Parser.State.ground)].action) {
// Print, call directly.
.print => win.print(@intCast(u21, c)) catch |err|
log.err("error processing terminal data: {}", .{err}),
// C0 execute, let our stream handle this one but otherwise
// continue since we're guaranteed to be back in ground.
.execute => win.terminal_stream.next(c) catch |err|
log.err("error processing terminal data: {}", .{err}),
// Otherwise, break out and go the slow path until we're
// back in ground.
else => break,
}
i += 1;
}
}
if (i < end) {
//log.warn("SLOW={}", .{end - i});
win.terminal_stream.nextSlice(buf[i..end]) catch |err|
log.err("error processing terminal data: {}", .{err});
}
}
fn ttyWrite(req: *libuv.WriteReq, status: i32) void {

View File

@ -6,6 +6,7 @@ const Parser = @This();
const std = @import("std");
const builtin = @import("builtin");
const trace = @import("tracy").trace;
const testing = std.testing;
const table = @import("parse_table.zig").table;
const osc = @import("osc.zig");
@ -212,6 +213,9 @@ pub fn init() Parser {
/// Up to 3 actions may need to be exected -- in order -- representing
/// the state exit, transition, and entry actions.
pub fn next(self: *Parser, c: u8) [3]?Action {
const tracy = trace(@src());
defer tracy.end();
// If we're processing UTF-8, we handle this manually.
if (self.state == .utf8) {
return .{ self.next_utf8(c), null, null };

View File

@ -22,6 +22,7 @@ const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const utf8proc = @import("utf8proc");
const trace = @import("tracy").trace;
const color = @import("color.zig");
const point = @import("point.zig");
const CircBuf = @import("circ_buf.zig").CircBuf;
@ -432,6 +433,9 @@ pub fn rowIterator(self: *Screen, tag: RowIndexTag) RowIterator {
/// Returns the row at the given index. This row is writable, although
/// only the active area should probably be written to.
pub fn getRow(self: *Screen, index: RowIndex) Row {
const tracy = trace(@src());
defer tracy.end();
// Get our offset into storage
const offset = index.toScreen(self).screen * (self.cols + 1);

View File

@ -464,7 +464,7 @@ pub fn print(self: *Terminal, c: u21) !void {
switch (width) {
// Single cell is very easy: just write in the cell
1 => _ = self.printCell(c),
1 => _ = @call(.{ .modifier = .always_inline }, self.printCell, .{c}),
// Wide character requires a spacer. We print this by
// using two cells: the first is flagged "wide" and has the
@ -505,6 +505,9 @@ pub fn print(self: *Terminal, c: u21) !void {
}
fn printCell(self: *Terminal, unmapped_c: u21) *Screen.Cell {
// const tracy = trace(@src());
// defer tracy.end();
const c = c: {
// TODO: non-utf8 handling, gr
@ -558,6 +561,9 @@ fn printCell(self: *Terminal, unmapped_c: u21) *Screen.Cell {
}
fn printWrap(self: *Terminal) !void {
const tracy = trace(@src());
defer tracy.end();
const row = self.screen.getRow(.{ .active = self.screen.cursor.y });
row.setWrapped(true);

View File

@ -7,6 +7,7 @@ const csi = @import("csi.zig");
const sgr = @import("sgr.zig");
pub const point = @import("point.zig");
pub const color = @import("color.zig");
pub const parse_table = @import("parse_table.zig");
pub const Charset = charsets.Charset;
pub const CharsetSlot = charsets.Slots;