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( | |
| 92 | 92 | tag: Tag, | |
| 93 | 93 | data: []const u8, | |
| 94 | 94 | ) !void { | |
| 95 | - | std.log.info("sending ipc message tag={s}", .{@tagName(tag)}); | |
| 96 | 95 | const header = Header{ | |
| 97 | 96 | .tag = tag, | |
| 98 | 97 | .len = @intCast(data.len), | |
| 99 | 98 | }; | |
| 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)); | |
| 101 | 103 | if (data.len > 0) { | |
| 102 | - | try list.appendSlice(alloc, data); | |
| 104 | + | list.appendSliceAssumeCapacity(data); | |
| 103 | 105 | } | |
| 104 | 106 | } | |
| 105 | 107 |
+9,
-2
| ... | ... | @@ -2558,7 +2558,11 @@ fn daemonLoop(daemon: *Daemon, server_sock_fd: i32, pty_fd: i32) !void { | |
| 2558 | 2558 | .read_buf = try ipc.SocketBuffer.init(daemon.alloc), | |
| 2559 | 2559 | .write_buf = undefined, | |
| 2560 | 2560 | }; | |
| 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); | |
| 2562 | 2566 | try daemon.clients.append(daemon.alloc, client); | |
| 2563 | 2567 | std.log.info( | |
| 2564 | 2568 | "client connected fd={d} total={d}", |
| ... | ... | @@ -2568,7 +2572,10 @@ fn daemonLoop(daemon: *Daemon, server_sock_fd: i32, pty_fd: i32) !void { | |
| 2568 | 2572 | ||
| 2569 | 2573 | const inp_flags = posix.POLL.IN | posix.POLL.HUP | posix.POLL.ERR | posix.POLL.NVAL; | |
| 2570 | 2574 | 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. | |
| 2572 | 2579 | var buf: [4096]u8 = undefined; | |
| 2573 | 2580 | const n_opt: ?usize = posix.read(pty_fd, &buf) catch |err| blk: { | |
| 2574 | 2581 | if (err == error.WouldBlock) break :blk null; |
+3,
-1
| ... | ... | @@ -197,7 +197,9 @@ const OSC_133_A = "\x1b]133;A"; | |
| 197 | 197 | /// makes the prompt invisible. | |
| 198 | 198 | /// See: https://github.com/neurosnap/zmx/issues/111 | |
| 199 | 199 | 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; | |
| 201 | 203 | if (std.mem.indexOf(u8, data, OSC_133_A) == null) return null; | |
| 202 | 204 | ||
| 203 | 205 | var result = std.ArrayList(u8).initCapacity(alloc, data.len + 200) catch return null; |