From 5929bf57cca6f17c528bfe215f8bec0e55d65398 Mon Sep 17 00:00:00 2001 From: Yorhel Date: Wed, 28 Jul 2021 20:12:50 +0200 Subject: Keep track of uncounted hard links to speed up refresh+delete operations --- src/model.zig | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/model.zig b/src/model.zig index 43e20a6..42f2a78 100644 --- a/src/model.zig +++ b/src/model.zig @@ -134,7 +134,7 @@ pub const Entry = packed struct { l.next = d.key_ptr.*.next; d.key_ptr.*.next = l; } - + inodes.addUncounted(l); } var it: ?*Dir = parent; @@ -173,10 +173,12 @@ pub const Entry = packed struct { d.value_ptr.nlink = 0; if (l.next == l) { _ = inodes.map.remove(l); + _ = inodes.uncounted.remove(l); inodes.total_blocks = saturateSub(inodes.total_blocks, self.blocks); } else { if (d.key_ptr.* == l) d.key_ptr.* = l.next; + inodes.addUncounted(l.next); // This is O(n), which in this context has the potential to // slow ncdu down to a crawl. But this function is only called // on refresh/delete operations and even then it's not common @@ -343,6 +345,13 @@ pub const inodes = struct { // the hard links are not counted as part of the parent directories yet. pub var total_blocks: Blocks = 0; + // List of nodes in 'map' with !counted, to speed up addAllStats(). + // If this list grows large relative to the number of nodes in 'map', then + // this list is cleared and uncounted_full is set instead, so that + // addAllStats() will do a full iteration over 'map'. + var uncounted = std.HashMap(*Link, void, HashContext, 80).init(main.allocator); + var uncounted_full = true; // start with true for the initial scan + const Inode = packed struct { // Whether this Inode is counted towards the parent directories. counted: bool, @@ -367,6 +376,15 @@ pub const inodes = struct { } }; + fn addUncounted(l: *Link) void { + if (uncounted_full) return; + if (uncounted.count() > map.count()/8) { + uncounted.clearAndFree(); + uncounted_full = true; + } else + (uncounted.getOrPut(l) catch unreachable).key_ptr.* = l; + } + // Add/remove this inode from the parent Dir sizes. When removing stats, // the list of *Links and their sizes and counts must be in the exact same // state as when the stats were added. Hence, any modification to the Link @@ -419,11 +437,17 @@ pub const inodes = struct { } } - // TODO: A version that doesn't have to iterate over the entire map, for - // smaller refresh/delete updates. pub fn addAllStats() void { - var it = map.iterator(); - while (it.next()) |e| setStats(e, true); + if (uncounted_full) { + var it = map.iterator(); + while (it.next()) |e| setStats(e, true); + } else { + var it = uncounted.iterator(); + while (it.next()) |u| if (map.getEntry(u.key_ptr.*)) |e| setStats(e, true); + } + uncounted_full = false; + if (uncounted.count() > 0) + uncounted.clearAndFree(); } }; -- cgit v1.2.3