authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-05-26 14:50:50-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-05-27 20:56:48-07:00
log7fe72d560d94fc379165ab46c9e568e6e684aa41
treedc18983c81629195bc2cd2fca816dc7dcaa12f33
parentd3b03ed64b9c3a4a963e6e5bef536c61c0e3e3c0

std.Progress: move global preallocations to thread memory

Instead of making static buffers configurable, let's pick strong defaults and then use the update thread's stack memory to store the preallocations. The thread uses a fairly shallow stack so this memory is otherwise unused. This also makes the data section of the executable smaller since it runtime allocates the memory when a `std.Progress` instance is allocated, and in the case that the process is not connected to a terminal, it never allocates the memory.

1 files changed, 60 insertions(+), 45 deletions(-)

lib/std/Progress.zig+60-45
...@@ -280,7 +280,6 @@ var global_progress: Progress = .{...@@ -280,7 +280,6 @@ var global_progress: Progress = .{
280 .draw_buffer = undefined,280 .draw_buffer = undefined,
281 .done = false,281 .done = false,
282282
283 // TODO: make these configurable and avoid including the globals in .data if unused
284 .node_parents = &node_parents_buffer,283 .node_parents = &node_parents_buffer,
285 .node_storage = &node_storage_buffer,284 .node_storage = &node_storage_buffer,
286 .node_freelist = &node_freelist_buffer,285 .node_freelist = &node_freelist_buffer,
...@@ -288,10 +287,10 @@ var global_progress: Progress = .{...@@ -288,10 +287,10 @@ var global_progress: Progress = .{
288 .node_end_index = 0,287 .node_end_index = 0,
289};288};
290289
291const default_node_storage_buffer_len = 200;290const node_storage_buffer_len = 200;
292var node_parents_buffer: [default_node_storage_buffer_len]Node.Parent = undefined;291var node_parents_buffer: [node_storage_buffer_len]Node.Parent = undefined;
293var node_storage_buffer: [default_node_storage_buffer_len]Node.Storage = undefined;292var node_storage_buffer: [node_storage_buffer_len]Node.Storage = undefined;
294var node_freelist_buffer: [default_node_storage_buffer_len]Node.OptionalIndex = undefined;293var node_freelist_buffer: [node_storage_buffer_len]Node.OptionalIndex = undefined;
295294
296var default_draw_buffer: [4096]u8 = undefined;295var default_draw_buffer: [4096]u8 = undefined;
297296
...@@ -391,14 +390,21 @@ fn wait(timeout_ns: u64) bool {...@@ -391,14 +390,21 @@ fn wait(timeout_ns: u64) bool {
391}390}
392391
393fn updateThreadRun() void {392fn updateThreadRun() void {
393 // Store this data in the thread so that it does not need to be part of the
394 // linker data of the main executable.
395 var serialized_buffer: Serialized.Buffer = undefined;
396
394 {397 {
395 const resize_flag = wait(global_progress.initial_delay_ns);398 const resize_flag = wait(global_progress.initial_delay_ns);
396 maybeUpdateSize(resize_flag);399 maybeUpdateSize(resize_flag);
397400
398 if (@atomicLoad(bool, &global_progress.done, .seq_cst))401 if (@atomicLoad(bool, &global_progress.done, .seq_cst)) {
402 stderr_mutex.lock();
403 defer stderr_mutex.unlock();
399 return clearTerminal();404 return clearTerminal();
405 }
400406
401 const buffer = computeRedraw();407 const buffer = computeRedraw(&serialized_buffer);
402 if (stderr_mutex.tryLock()) {408 if (stderr_mutex.tryLock()) {
403 defer stderr_mutex.unlock();409 defer stderr_mutex.unlock();
404 write(buffer);410 write(buffer);
...@@ -409,10 +415,13 @@ fn updateThreadRun() void {...@@ -409,10 +415,13 @@ fn updateThreadRun() void {
409 const resize_flag = wait(global_progress.refresh_rate_ns);415 const resize_flag = wait(global_progress.refresh_rate_ns);
410 maybeUpdateSize(resize_flag);416 maybeUpdateSize(resize_flag);
411417
412 if (@atomicLoad(bool, &global_progress.done, .seq_cst))418 if (@atomicLoad(bool, &global_progress.done, .seq_cst)) {
419 stderr_mutex.lock();
420 defer stderr_mutex.unlock();
413 return clearTerminal();421 return clearTerminal();
422 }
414423
415 const buffer = computeRedraw();424 const buffer = computeRedraw(&serialized_buffer);
416 if (stderr_mutex.tryLock()) {425 if (stderr_mutex.tryLock()) {
417 defer stderr_mutex.unlock();426 defer stderr_mutex.unlock();
418 write(buffer);427 write(buffer);
...@@ -433,13 +442,17 @@ pub fn unlockStdErr() void {...@@ -433,13 +442,17 @@ pub fn unlockStdErr() void {
433}442}
434443
435fn ipcThreadRun(fd: posix.fd_t) anyerror!void {444fn ipcThreadRun(fd: posix.fd_t) anyerror!void {
445 // Store this data in the thread so that it does not need to be part of the
446 // linker data of the main executable.
447 var serialized_buffer: Serialized.Buffer = undefined;
448
436 {449 {
437 _ = wait(global_progress.initial_delay_ns);450 _ = wait(global_progress.initial_delay_ns);
438451
439 if (@atomicLoad(bool, &global_progress.done, .seq_cst))452 if (@atomicLoad(bool, &global_progress.done, .seq_cst))
440 return;453 return;
441454
442 const serialized = serialize();455 const serialized = serialize(&serialized_buffer);
443 writeIpc(fd, serialized) catch |err| switch (err) {456 writeIpc(fd, serialized) catch |err| switch (err) {
444 error.BrokenPipe => return,457 error.BrokenPipe => return,
445 };458 };
...@@ -451,7 +464,7 @@ fn ipcThreadRun(fd: posix.fd_t) anyerror!void {...@@ -451,7 +464,7 @@ fn ipcThreadRun(fd: posix.fd_t) anyerror!void {
451 if (@atomicLoad(bool, &global_progress.done, .seq_cst))464 if (@atomicLoad(bool, &global_progress.done, .seq_cst))
452 return clearTerminal();465 return clearTerminal();
453466
454 const serialized = serialize();467 const serialized = serialize(&serialized_buffer);
455 writeIpc(fd, serialized) catch |err| switch (err) {468 writeIpc(fd, serialized) catch |err| switch (err) {
456 error.BrokenPipe => return,469 error.BrokenPipe => return,
457 };470 };
...@@ -511,17 +524,18 @@ const Children = struct {...@@ -511,17 +524,18 @@ const Children = struct {
511 sibling: Node.OptionalIndex,524 sibling: Node.OptionalIndex,
512};525};
513526
514// TODO make this configurable
515var serialized_node_parents_buffer: [default_node_storage_buffer_len]Node.Parent = undefined;
516var serialized_node_storage_buffer: [default_node_storage_buffer_len]Node.Storage = undefined;
517var serialized_node_map_buffer: [default_node_storage_buffer_len]Node.Index = undefined;
518
519const Serialized = struct {527const Serialized = struct {
520 parents: []Node.Parent,528 parents: []Node.Parent,
521 storage: []Node.Storage,529 storage: []Node.Storage,
530
531 const Buffer = struct {
532 parents: [node_storage_buffer_len]Node.Parent,
533 storage: [node_storage_buffer_len]Node.Storage,
534 map: [node_storage_buffer_len]Node.Index,
535 };
522};536};
523537
524fn serialize() Serialized {538fn serialize(serialized_buffer: *Serialized.Buffer) Serialized {
525 var serialized_len: usize = 0;539 var serialized_len: usize = 0;
526 var any_ipc = false;540 var any_ipc = false;
527541
...@@ -533,15 +547,15 @@ fn serialize() Serialized {...@@ -533,15 +547,15 @@ fn serialize() Serialized {
533 for (node_parents, node_storage, 0..) |*parent_ptr, *storage_ptr, i| {547 for (node_parents, node_storage, 0..) |*parent_ptr, *storage_ptr, i| {
534 var begin_parent = @atomicLoad(Node.Parent, parent_ptr, .seq_cst);548 var begin_parent = @atomicLoad(Node.Parent, parent_ptr, .seq_cst);
535 while (begin_parent != .unused) {549 while (begin_parent != .unused) {
536 const dest_storage = &serialized_node_storage_buffer[serialized_len];550 const dest_storage = &serialized_buffer.storage[serialized_len];
537 @memcpy(&dest_storage.name, &storage_ptr.name);551 @memcpy(&dest_storage.name, &storage_ptr.name);
538 dest_storage.completed_count = @atomicLoad(u32, &storage_ptr.completed_count, .monotonic);552 dest_storage.completed_count = @atomicLoad(u32, &storage_ptr.completed_count, .monotonic);
539 dest_storage.estimated_total_count = @atomicLoad(u32, &storage_ptr.estimated_total_count, .monotonic);553 dest_storage.estimated_total_count = @atomicLoad(u32, &storage_ptr.estimated_total_count, .monotonic);
540 const end_parent = @atomicLoad(Node.Parent, parent_ptr, .seq_cst);554 const end_parent = @atomicLoad(Node.Parent, parent_ptr, .seq_cst);
541 if (begin_parent == end_parent) {555 if (begin_parent == end_parent) {
542 any_ipc = any_ipc or (dest_storage.getIpcFd() != null);556 any_ipc = any_ipc or (dest_storage.getIpcFd() != null);
543 serialized_node_parents_buffer[serialized_len] = begin_parent;557 serialized_buffer.parents[serialized_len] = begin_parent;
544 serialized_node_map_buffer[i] = @enumFromInt(serialized_len);558 serialized_buffer.map[i] = @enumFromInt(serialized_len);
545 serialized_len += 1;559 serialized_len += 1;
546 break;560 break;
547 }561 }
...@@ -551,29 +565,29 @@ fn serialize() Serialized {...@@ -551,29 +565,29 @@ fn serialize() Serialized {
551 }565 }
552566
553 // Remap parents to point inside serialized arrays.567 // Remap parents to point inside serialized arrays.
554 for (serialized_node_parents_buffer[0..serialized_len]) |*parent| {568 for (serialized_buffer.parents[0..serialized_len]) |*parent| {
555 parent.* = switch (parent.*) {569 parent.* = switch (parent.*) {
556 .unused => unreachable,570 .unused => unreachable,
557 .none => .none,571 .none => .none,
558 _ => |p| serialized_node_map_buffer[@intFromEnum(p)].toParent(),572 _ => |p| serialized_buffer.map[@intFromEnum(p)].toParent(),
559 };573 };
560 }574 }
561575
562 // Find nodes which correspond to child processes.576 // Find nodes which correspond to child processes.
563 if (any_ipc)577 if (any_ipc)
564 serialized_len = serializeIpc(serialized_len);578 serialized_len = serializeIpc(serialized_len, serialized_buffer);
565579
566 return .{580 return .{
567 .parents = serialized_node_parents_buffer[0..serialized_len],581 .parents = serialized_buffer.parents[0..serialized_len],
568 .storage = serialized_node_storage_buffer[0..serialized_len],582 .storage = serialized_buffer.storage[0..serialized_len],
569 };583 };
570}584}
571585
572var parents_copy: [default_node_storage_buffer_len]Node.Parent = undefined;586var parents_copy: [node_storage_buffer_len]Node.Parent = undefined;
573var storage_copy: [default_node_storage_buffer_len]Node.Storage = undefined;587var storage_copy: [node_storage_buffer_len]Node.Storage = undefined;
574var ipc_metadata_copy: [default_node_storage_buffer_len]SavedMetadata = undefined;588var ipc_metadata_copy: [node_storage_buffer_len]SavedMetadata = undefined;
575589
576var ipc_metadata: [default_node_storage_buffer_len]SavedMetadata = undefined;590var ipc_metadata: [node_storage_buffer_len]SavedMetadata = undefined;
577var ipc_metadata_len: u16 = 0;591var ipc_metadata_len: u16 = 0;
578592
579const SavedMetadata = struct {593const SavedMetadata = struct {
...@@ -597,7 +611,7 @@ const SavedMetadata = struct {...@@ -597,7 +611,7 @@ const SavedMetadata = struct {
597 }611 }
598};612};
599613
600fn serializeIpc(start_serialized_len: usize) usize {614fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buffer) usize {
601 var serialized_len = start_serialized_len;615 var serialized_len = start_serialized_len;
602 var pipe_buf: [2 * 4096]u8 align(4) = undefined;616 var pipe_buf: [2 * 4096]u8 align(4) = undefined;
603617
...@@ -605,8 +619,8 @@ fn serializeIpc(start_serialized_len: usize) usize {...@@ -605,8 +619,8 @@ fn serializeIpc(start_serialized_len: usize) usize {
605 ipc_metadata_len = 0;619 ipc_metadata_len = 0;
606620
607 main_loop: for (621 main_loop: for (
608 serialized_node_parents_buffer[0..serialized_len],622 serialized_buffer.parents[0..serialized_len],
609 serialized_node_storage_buffer[0..serialized_len],623 serialized_buffer.storage[0..serialized_len],
610 0..,624 0..,
611 ) |main_parent, *main_storage, main_index| {625 ) |main_parent, *main_storage, main_index| {
612 if (main_parent == .unused) continue;626 if (main_parent == .unused) continue;
...@@ -628,7 +642,7 @@ fn serializeIpc(start_serialized_len: usize) usize {...@@ -628,7 +642,7 @@ fn serializeIpc(start_serialized_len: usize) usize {
628 // Ignore all but the last message on the pipe.642 // Ignore all but the last message on the pipe.
629 var input: []align(2) u8 = pipe_buf[0..bytes_read];643 var input: []align(2) u8 = pipe_buf[0..bytes_read];
630 if (input.len == 0) {644 if (input.len == 0) {
631 serialized_len = useSavedIpcData(serialized_len, main_storage, main_index, old_ipc_metadata);645 serialized_len = useSavedIpcData(serialized_len, serialized_buffer, main_storage, main_index, old_ipc_metadata);
632 continue;646 continue;
633 }647 }
634648
...@@ -636,7 +650,7 @@ fn serializeIpc(start_serialized_len: usize) usize {...@@ -636,7 +650,7 @@ fn serializeIpc(start_serialized_len: usize) usize {
636 if (input.len < 4) {650 if (input.len < 4) {
637 std.log.warn("short read: {d} out of 4 header bytes", .{input.len});651 std.log.warn("short read: {d} out of 4 header bytes", .{input.len});
638 // TODO keep track of the short read to trash odd bytes with the next read652 // TODO keep track of the short read to trash odd bytes with the next read
639 serialized_len = useSavedIpcData(serialized_len, main_storage, main_index, old_ipc_metadata);653 serialized_len = useSavedIpcData(serialized_len, serialized_buffer, main_storage, main_index, old_ipc_metadata);
640 continue :main_loop;654 continue :main_loop;
641 }655 }
642 const subtree_len = std.mem.readInt(u32, input[0..4], .little);656 const subtree_len = std.mem.readInt(u32, input[0..4], .little);
...@@ -644,7 +658,7 @@ fn serializeIpc(start_serialized_len: usize) usize {...@@ -644,7 +658,7 @@ fn serializeIpc(start_serialized_len: usize) usize {
644 if (input.len < expected_bytes) {658 if (input.len < expected_bytes) {
645 std.log.warn("short read: {d} out of {d} ({d} nodes)", .{ input.len, expected_bytes, subtree_len });659 std.log.warn("short read: {d} out of {d} ({d} nodes)", .{ input.len, expected_bytes, subtree_len });
646 // TODO keep track of the short read to trash odd bytes with the next read660 // TODO keep track of the short read to trash odd bytes with the next read
647 serialized_len = useSavedIpcData(serialized_len, main_storage, main_index, old_ipc_metadata);661 serialized_len = useSavedIpcData(serialized_len, serialized_buffer, main_storage, main_index, old_ipc_metadata);
648 continue :main_loop;662 continue :main_loop;
649 }663 }
650 if (input.len > expected_bytes) {664 if (input.len > expected_bytes) {
...@@ -672,12 +686,12 @@ fn serializeIpc(start_serialized_len: usize) usize {...@@ -672,12 +686,12 @@ fn serializeIpc(start_serialized_len: usize) usize {
672 copyRoot(main_storage, &storage[0]);686 copyRoot(main_storage, &storage[0]);
673687
674 // Copy the rest of the tree to the end.688 // Copy the rest of the tree to the end.
675 @memcpy(serialized_node_storage_buffer[serialized_len..][0 .. storage.len - 1], storage[1..]);689 @memcpy(serialized_buffer.storage[serialized_len..][0 .. storage.len - 1], storage[1..]);
676690
677 // Patch up parent pointers taking into account how the subtree is mounted.691 // Patch up parent pointers taking into account how the subtree is mounted.
678 serialized_node_parents_buffer[serialized_len] = .none;692 serialized_buffer.parents[serialized_len] = .none;
679693
680 for (serialized_node_parents_buffer[serialized_len..][0 .. parents.len - 1], parents[1..]) |*dest, p| {694 for (serialized_buffer.parents[serialized_len..][0 .. parents.len - 1], parents[1..]) |*dest, p| {
681 dest.* = switch (p) {695 dest.* = switch (p) {
682 // Fix bad data so the rest of the code does not see `unused`.696 // Fix bad data so the rest of the code does not see `unused`.
683 .none, .unused => .none,697 .none, .unused => .none,
...@@ -693,8 +707,8 @@ fn serializeIpc(start_serialized_len: usize) usize {...@@ -693,8 +707,8 @@ fn serializeIpc(start_serialized_len: usize) usize {
693 }707 }
694708
695 // Save a copy in case any pipes are empty on the next update.709 // Save a copy in case any pipes are empty on the next update.
696 @memcpy(parents_copy[0..serialized_len], serialized_node_parents_buffer[0..serialized_len]);710 @memcpy(parents_copy[0..serialized_len], serialized_buffer.parents[0..serialized_len]);
697 @memcpy(storage_copy[0..serialized_len], serialized_node_storage_buffer[0..serialized_len]);711 @memcpy(storage_copy[0..serialized_len], serialized_buffer.storage[0..serialized_len]);
698 @memcpy(ipc_metadata_copy[0..ipc_metadata_len], ipc_metadata[0..ipc_metadata_len]);712 @memcpy(ipc_metadata_copy[0..ipc_metadata_len], ipc_metadata[0..ipc_metadata_len]);
699713
700 return serialized_len;714 return serialized_len;
...@@ -718,6 +732,7 @@ fn findOld(ipc_fd: posix.fd_t, old_metadata: []const SavedMetadata) ?*const Save...@@ -718,6 +732,7 @@ fn findOld(ipc_fd: posix.fd_t, old_metadata: []const SavedMetadata) ?*const Save
718732
719fn useSavedIpcData(733fn useSavedIpcData(
720 start_serialized_len: usize,734 start_serialized_len: usize,
735 serialized_buffer: *Serialized.Buffer,
721 main_storage: *Node.Storage,736 main_storage: *Node.Storage,
722 main_index: usize,737 main_index: usize,
723 old_metadata: []const SavedMetadata,738 old_metadata: []const SavedMetadata,
...@@ -746,9 +761,9 @@ fn useSavedIpcData(...@@ -746,9 +761,9 @@ fn useSavedIpcData(
746761
747 copyRoot(main_storage, &storage_copy[old_main_index]);762 copyRoot(main_storage, &storage_copy[old_main_index]);
748763
749 @memcpy(serialized_node_storage_buffer[start_serialized_len..][0..storage.len], storage);764 @memcpy(serialized_buffer.storage[start_serialized_len..][0..storage.len], storage);
750765
751 for (serialized_node_parents_buffer[start_serialized_len..][0..parents.len], parents) |*dest, p| {766 for (serialized_buffer.parents[start_serialized_len..][0..parents.len], parents) |*dest, p| {
752 dest.* = switch (p) {767 dest.* = switch (p) {
753 .none, .unused => .none,768 .none, .unused => .none,
754 _ => |prev| @enumFromInt(if (@intFromEnum(prev) == old_main_index)769 _ => |prev| @enumFromInt(if (@intFromEnum(prev) == old_main_index)
...@@ -761,14 +776,14 @@ fn useSavedIpcData(...@@ -761,14 +776,14 @@ fn useSavedIpcData(
761 return start_serialized_len + storage.len;776 return start_serialized_len + storage.len;
762}777}
763778
764fn computeRedraw() []u8 {779fn computeRedraw(serialized_buffer: *Serialized.Buffer) []u8 {
765 const serialized = serialize();780 const serialized = serialize(serialized_buffer);
766781
767 // Now we can analyze our copy of the graph without atomics, reconstructing782 // Now we can analyze our copy of the graph without atomics, reconstructing
768 // children lists which do not exist in the canonical data. These are783 // children lists which do not exist in the canonical data. These are
769 // needed for tree traversal below.784 // needed for tree traversal below.
770785
771 var children_buffer: [default_node_storage_buffer_len]Children = undefined;786 var children_buffer: [node_storage_buffer_len]Children = undefined;
772 const children = children_buffer[0..serialized.parents.len];787 const children = children_buffer[0..serialized.parents.len];
773788
774 @memset(children, .{ .child = .none, .sibling = .none });789 @memset(children, .{ .child = .none, .sibling = .none });