Commit 4c2924c

Eric Bower  ·  2026-06-19 23:07:19 -0400 EDT
parent 79169a0
perf: reduce allocations in the hot-path of the pty-proxy
3 files changed,  +17, -6
+5, -3
......@@ -92,14 +92,16 @@ pub fn appendMessage(
9292 tag: Tag,
9393 data: []const u8,
9494 ) !void {
95- std.log.info("sending ipc message tag={s}", .{@tagName(tag)});
9695 const header = Header{
9796 .tag = tag,
9897 .len = @intCast(data.len),
9998 };
100- try list.appendSlice(alloc, std.mem.asBytes(&header));
99+ // Guarantee capacity for header + payload in one check to avoid
100+ // intermediate realloc between the two appends on the hot path.
101+ try list.ensureTotalCapacity(alloc, list.items.len + @sizeOf(Header) + data.len);
102+ list.appendSliceAssumeCapacity(std.mem.asBytes(&header));
101103 if (data.len > 0) {
102- try list.appendSlice(alloc, data);
104+ list.appendSliceAssumeCapacity(data);
103105 }
104106 }
105107
+9, -2
......@@ -2558,7 +2558,11 @@ fn daemonLoop(daemon: *Daemon, server_sock_fd: i32, pty_fd: i32) !void {
25582558 .read_buf = try ipc.SocketBuffer.init(daemon.alloc),
25592559 .write_buf = undefined,
25602560 };
2561- client.write_buf = try std.ArrayList(u8).initCapacity(client.alloc, 4096);
2561+ // 64KB initial capacity lets ~15 broadcast cycles (N_TTY_BUF_SIZE reads
2562+ // * header) accumulate before the first ArrayList growth. The write
2563+ // buffer is userspace-only: it drains via POLLOUT to the client socket,
2564+ // which has no corresponding kernel-imposed per-write limit.
2565+ client.write_buf = try std.ArrayList(u8).initCapacity(client.alloc, 65536);
25622566 try daemon.clients.append(daemon.alloc, client);
25632567 std.log.info(
25642568 "client connected fd={d} total={d}",
......@@ -2568,7 +2572,10 @@ fn daemonLoop(daemon: *Daemon, server_sock_fd: i32, pty_fd: i32) !void {
25682572
25692573 const inp_flags = posix.POLL.IN | posix.POLL.HUP | posix.POLL.ERR | posix.POLL.NVAL;
25702574 if (poll_fds.items[1].revents & inp_flags != 0) {
2571- // Read from PTY
2575+ // Read from PTY. Buffer is sized to N_TTY_BUF_SIZE (4096): the hard
2576+ // kernel limit for the N_TTY line discipline. A larger buffer doesn't
2577+ // help: each read() from a PTY master returns at most 4096 bytes
2578+ // regardless of the userspace buffer size.
25722579 var buf: [4096]u8 = undefined;
25732580 const n_opt: ?usize = posix.read(pty_fd, &buf) catch |err| blk: {
25742581 if (err == error.WouldBlock) break :blk null;
+3, -1
......@@ -197,7 +197,9 @@ const OSC_133_A = "\x1b]133;A";
197197 /// makes the prompt invisible.
198198 /// See: https://github.com/neurosnap/zmx/issues/111
199199 pub fn rewritePromptRedraw(alloc: std.mem.Allocator, data: []const u8) ?[]const u8 {
200- // Quick scan: is there any OSC 133;A in this chunk?
200+ // Fast-path: most PTY output has no escape sequences at all. A scalar
201+ // byte scan for ESC is cheaper than the full string indexOf below.
202+ if (std.mem.indexOfScalar(u8, data, '\x1b') == null) return null;
201203 if (std.mem.indexOf(u8, data, OSC_133_A) == null) return null;
202204
203205 var result = std.ArrayList(u8).initCapacity(alloc, data.len + 200) catch return null;