Files
ghostty/src/datastruct/main.zig
Mitchell Hashimoto e01e75bbb2 terminal: don't touch me! keep the page pool free list unobtrusive
This replaces the `std.heap.MemoryPool` used for page buffers with
a custom pool called `UntouchedPool`. This keeps its free list in a side
array and never reads/writes items until `create()`. This means that
demand-driven allocations (like mmaped pages) don't incur physical costs
until they're actually used.

The standard `std.heap.MemoryPool` uses an intrusive linked list for
its items which causes every item to be touched, which forces a full
page-in of memory.

It turns out we also had a lot of assertions and logic to work around
this in various ways (size of rows, asserting we overwrite the free
list entry, etc.) that we can now remove because of this.

For an 80x24 terminal on macOS (16 KB pages):

| Per terminal                 | Before   | After    |
|------------------------------|----------|----------|
| Page-list memory dirty       | 128 KiB  | 48 KiB   |
| Process phys_footprint delta | 143 KiB  | 62 KiB   |
| Page-list virtual size       | 2208 KiB | 1600 KiB |

The remaining 48 KB is the active page, because we sprinkle metadata
around the page which forces every page to be paged in. I'm going to
follow this up with some work trying to move all our metadata to the
front of the page so we only page one in until the rest is needed,
but not sure if its achievable.

Micro-benchmarks on the pool show that its twice the speed (slower) to
create/free due to the side list, but in an actual `+terminal-stream`
benchmark churning through pages, there is no measurable difference. I
think its a good trade.
2026-09-02 20:52:40 -07:00

25 lines
1007 B
Zig

//! The datastruct package contains data structures or anything closely
//! related to data structures.
const blocking_queue = @import("blocking_queue.zig");
const cache_table = @import("cache_table.zig");
const circ_buf = @import("circ_buf.zig");
const intrusive_linked_list = @import("intrusive_linked_list.zig");
const split_tree = @import("split_tree.zig");
pub const BlockingQueue = blocking_queue.BlockingQueue;
pub const CacheTable = cache_table.CacheTable;
pub const CircBuf = circ_buf.CircBuf;
pub const IntrusiveDoublyLinkedList = intrusive_linked_list.DoublyLinkedList;
pub const LimitedAllocator = @import("limited_allocator.zig").LimitedAllocator;
pub const MessageData = @import("message_data.zig").MessageData;
pub const SplitTree = split_tree.SplitTree;
pub const UntouchedPool = @import("untouched_pool.zig").UntouchedPool;
pub const WasmPagePool = @import("wasm_page_pool.zig").WasmPagePool;
test {
@import("std").testing.refAllDecls(@This());
_ = @import("comparison.zig");
}