From 41072fc05c4fd83f42aaf9008d2b16f1ccdf83cc Mon Sep 17 00:00:00 2001 From: Maxwell Elliott Date: Tue, 3 Nov 2026 13:39:34 +0000 Subject: [PATCH] perf: release dropped attribute storage after prehashing each queried rule prehash_rule kept only tags and visibility via Vec::retain, which leaves the decoded buffer allocated. Attribute is 464 bytes and a queried rule carries dozens of attributes, so every rule pinned tens of KB of dead capacity until hashing finished. shrink_to_fit after the retain returns it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01KJs3XytsGbjrXc37W1GZP5 --- src/hash.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/hash.rs b/src/hash.rs index 78d693e5..69dbe922 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -1762,9 +1762,10 @@ fn prehash_rule(rule: &mut Rule, ignored: &HashSet) -> DigestBytes { let digest = rule_definition_digest(rule, ignored); rule.attribute .retain(|attribute| attribute.name == "tags" || attribute.name == "visibility"); - if rule.attribute.is_empty() { - rule.attribute = Vec::new(); - } + // `retain` keeps the buffer it was decoded into. An `Attribute` is several hundred bytes and a + // rule carries dozens of them (every implicit attribute is in the proto), so without this each + // queried rule pins tens of kilobytes of dead capacity until hashing ends. + rule.attribute.shrink_to_fit(); digest } @@ -2862,6 +2863,8 @@ esac .collect::>(), ["tags", "visibility"] ); + // The dropped attributes' slots are released, not just their contents. + assert_eq!(rule.attribute.capacity(), rule.attribute.len()); } #[test]