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
1@@ -92,14 +92,16 @@ pub fn appendMessage(
2 tag: Tag,
3 data: []const u8,
4 ) !void {
5- std.log.info("sending ipc message tag={s}", .{@tagName(tag)});
6 const header = Header{
7 .tag = tag,
8 .len = @intCast(data.len),
9 };
10- try list.appendSlice(alloc, std.mem.asBytes(&header));
11+ // Guarantee capacity for header + payload in one check to avoid
12+ // intermediate realloc between the two appends on the hot path.
13+ try list.ensureTotalCapacity(alloc, list.items.len + @sizeOf(Header) + data.len);
14+ list.appendSliceAssumeCapacity(std.mem.asBytes(&header));
15 if (data.len > 0) {
16- try list.appendSlice(alloc, data);
17+ list.appendSliceAssumeCapacity(data);
18 }
19 }
20
+9,
-2
1@@ -2558,7 +2558,11 @@ fn daemonLoop(daemon: *Daemon, server_sock_fd: i32, pty_fd: i32) !void {
2 .read_buf = try ipc.SocketBuffer.init(daemon.alloc),
3 .write_buf = undefined,
4 };
5- client.write_buf = try std.ArrayList(u8).initCapacity(client.alloc, 4096);
6+ // 64KB initial capacity lets ~15 broadcast cycles (N_TTY_BUF_SIZE reads
7+ // * header) accumulate before the first ArrayList growth. The write
8+ // buffer is userspace-only: it drains via POLLOUT to the client socket,
9+ // which has no corresponding kernel-imposed per-write limit.
10+ client.write_buf = try std.ArrayList(u8).initCapacity(client.alloc, 65536);
11 try daemon.clients.append(daemon.alloc, client);
12 std.log.info(
13 "client connected fd={d} total={d}",
14@@ -2568,7 +2572,10 @@ fn daemonLoop(daemon: *Daemon, server_sock_fd: i32, pty_fd: i32) !void {
15
16 const inp_flags = posix.POLL.IN | posix.POLL.HUP | posix.POLL.ERR | posix.POLL.NVAL;
17 if (poll_fds.items[1].revents & inp_flags != 0) {
18- // Read from PTY
19+ // Read from PTY. Buffer is sized to N_TTY_BUF_SIZE (4096): the hard
20+ // kernel limit for the N_TTY line discipline. A larger buffer doesn't
21+ // help: each read() from a PTY master returns at most 4096 bytes
22+ // regardless of the userspace buffer size.
23 var buf: [4096]u8 = undefined;
24 const n_opt: ?usize = posix.read(pty_fd, &buf) catch |err| blk: {
25 if (err == error.WouldBlock) break :blk null;
+3,
-1
1@@ -197,7 +197,9 @@ const OSC_133_A = "\x1b]133;A";
2 /// makes the prompt invisible.
3 /// See: https://github.com/neurosnap/zmx/issues/111
4 pub fn rewritePromptRedraw(alloc: std.mem.Allocator, data: []const u8) ?[]const u8 {
5- // Quick scan: is there any OSC 133;A in this chunk?
6+ // Fast-path: most PTY output has no escape sequences at all. A scalar
7+ // byte scan for ESC is cheaper than the full string indexOf below.
8+ if (std.mem.indexOfScalar(u8, data, '\x1b') == null) return null;
9 if (std.mem.indexOf(u8, data, OSC_133_A) == null) return null;
10
11 var result = std.ArrayList(u8).initCapacity(alloc, data.len + 200) catch return null;