terminal/new: using arena + pool is faster for page init

This commit is contained in:
Mitchell Hashimoto
2024-02-23 08:05:37 -08:00
parent f929c86d18
commit f2d4b64032
4 changed files with 40 additions and 26 deletions

View File

@ -9,6 +9,7 @@ const assert = std.debug.assert;
const point = @import("point.zig");
const pagepkg = @import("page.zig");
const size = @import("size.zig");
const OffsetBuf = size.OffsetBuf;
const Page = pagepkg.Page;
/// The number of PageList.Nodes we preheat the pool with. A node is
@ -41,12 +42,17 @@ const List = std.DoublyLinkedList(Page);
/// The memory pool we get page nodes from.
const Pool = std.heap.MemoryPool(List.Node);
const std_layout = Page.layout(Page.std_capacity);
const PagePool = std.heap.MemoryPoolAligned([std_layout.total_size]u8, std.mem.page_size);
/// The allocator to use for pages.
alloc: Allocator,
/// The memory pool we get page nodes for the linked list from.
pool: Pool,
page_pool: PagePool,
/// The list of pages in the screen.
pages: List,
@ -88,15 +94,15 @@ pub fn init(
var pool = try Pool.initPreheated(alloc, page_preheat);
errdefer pool.deinit();
var page_pool = try PagePool.initPreheated(std.heap.page_allocator, page_preheat);
errdefer page_pool.deinit();
var page = try pool.create();
// no errdefer because the pool deinit will clean up the page
const page_buf = OffsetBuf.init(try page_pool.create());
page.* = .{
.data = try Page.init(.{
.cols = cols,
.rows = @max(rows, page_min_rows),
.styles = page_default_styles,
}),
.data = Page.initBuf(page_buf, std_layout),
};
errdefer page.data.deinit(alloc);
page.data.size.rows = rows;
@ -122,6 +128,7 @@ pub fn init(
.cols = cols,
.rows = rows,
.pool = pool,
.page_pool = page_pool,
.pages = page_list,
.viewport = .{ .active = {} },
.active = .{ .page = page },
@ -131,7 +138,7 @@ pub fn init(
pub fn deinit(self: *PageList) void {
// Deallocate all the pages. We don't need to deallocate the list or
// nodes because they all reside in the pool.
while (self.pages.popFirst()) |node| node.data.deinit();
self.page_pool.deinit();
self.pool.deinit();
}
@ -237,12 +244,10 @@ fn createPage(self: *PageList) !*List.Node {
var page = try self.pool.create();
errdefer page.data.deinit();
const page_buf = OffsetBuf.init(try self.page_pool.create());
page.* = .{
.data = try Page.init(.{
.cols = self.cols,
.rows = @max(self.rows, page_min_rows),
.styles = page_default_styles,
}),
.data = Page.initBuf(page_buf, std_layout),
};
page.data.size.rows = 0;

View File

@ -1,8 +0,0 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
test {
const testing = std.testing;
try testing.expect(false);
}

View File

@ -2,7 +2,6 @@ const builtin = @import("builtin");
const page = @import("page.zig");
pub const PageList = @import("PageList.zig");
pub const PagePool = @import("PagePool.zig");
pub const Terminal = @import("Terminal.zig");
pub const Page = page.Page;

View File

@ -1,6 +1,7 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const testing = std.testing;
const color = @import("../color.zig");
const sgr = @import("../sgr.zig");
const style = @import("style.zig");
@ -95,8 +96,8 @@ pub const Page = struct {
/// requirements. This is enough to support a very large number of cells.
/// The standard capacity is chosen as the fast-path for allocation.
pub const std_capacity: Capacity = .{
.cols = 120,
.rows = 520,
.cols = 250,
.rows = 250,
.styles = 128,
.grapheme_bytes = 1024,
};
@ -145,6 +146,13 @@ pub const Page = struct {
errdefer std.os.munmap(backing);
const buf = OffsetBuf.init(backing);
return initBuf(buf, l);
}
/// Initialize a new page using the given backing memory.
/// It is up to the caller to not call deinit on these pages.
pub fn initBuf(buf: OffsetBuf, l: Layout) Page {
const cap = l.capacity;
const rows = buf.member(Row, l.rows_start);
const cells = buf.member(Cell, l.cells_start);
@ -160,7 +168,7 @@ pub const Page = struct {
}
return .{
.memory = backing,
.memory = @alignCast(buf.start()[0..l.total_size]),
.rows = rows,
.cells = cells,
.styles = style.Set.init(
@ -219,7 +227,7 @@ pub const Page = struct {
return .{ .row = row, .cell = cell };
}
const Layout = struct {
pub const Layout = struct {
total_size: usize,
rows_start: usize,
cells_start: usize,
@ -229,11 +237,12 @@ pub const Page = struct {
grapheme_alloc_layout: GraphemeAlloc.Layout,
grapheme_map_start: usize,
grapheme_map_layout: GraphemeMap.Layout,
capacity: Capacity,
};
/// The memory layout for a page given a desired minimum cols
/// and rows size.
fn layout(cap: Capacity) Layout {
pub fn layout(cap: Capacity) Layout {
const rows_start = 0;
const rows_end = rows_start + (cap.rows * @sizeOf(Row));
@ -266,6 +275,7 @@ pub const Page = struct {
.grapheme_alloc_layout = grapheme_alloc_layout,
.grapheme_map_start = grapheme_map_start,
.grapheme_map_layout = grapheme_map_layout,
.capacity = cap,
};
}
};
@ -343,6 +353,15 @@ pub const Cell = packed struct(u64) {
// });
// }
test "Page std size" {
// We want to ensure that the standard capacity is what we
// expect it to be. Changing this is fine but should be done with care
// so we fail a test if it changes.
const total_size = Page.layout(Page.std_capacity).total_size;
try testing.expectEqual(@as(usize, 524_288), total_size); // 512 KiB
//const pages = total_size / std.mem.page_size;
}
test "Page init" {
var page = try Page.init(.{
.cols = 120,
@ -353,7 +372,6 @@ test "Page init" {
}
test "Page read and write cells" {
const testing = std.testing;
var page = try Page.init(.{
.cols = 10,
.rows = 10,