From fbd14075974f9b25ecf07ceae197e252cd21346d Mon Sep 17 00:00:00 2001 From: Eric Anderson Date: Thu, 14 Aug 2025 07:23:01 -0700 Subject: [PATCH] api: Reduce allocations of Attributes.Builder Modifying existing Attributes was virtually guaranteed to allocate within build(), just because `base` was not considered for the new map size. Discard was also allocation-heavy because it often created a new map. Using a regular copy-on-write approach is enough to avoid the unnecessary allocations in both cases. This was noticed in a profile that included xds's AddressFilter.setPathFilter(), where Attributes.Builder.build() allocated 6x the memory of Attributes.Builder.data(). b/435208946#comment41 --- api/src/main/java/io/grpc/Attributes.java | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/api/src/main/java/io/grpc/Attributes.java b/api/src/main/java/io/grpc/Attributes.java index de00e63554c..c8550d176b4 100644 --- a/api/src/main/java/io/grpc/Attributes.java +++ b/api/src/main/java/io/grpc/Attributes.java @@ -215,6 +215,7 @@ public int hashCode() { * The helper class to build an Attributes instance. */ public static final class Builder { + // Exactly one of base and newdata will be set private Attributes base; private IdentityHashMap, Object> newdata; @@ -225,8 +226,11 @@ private Builder(Attributes base) { private IdentityHashMap, Object> data(int size) { if (newdata == null) { - newdata = new IdentityHashMap<>(size); + newdata = new IdentityHashMap<>(base.data.size() + size); + newdata.putAll(base.data); + base = null; } + assert base == null; return newdata; } @@ -243,12 +247,11 @@ public Builder set(Key key, T value) { * @return this */ public Builder discard(Key key) { - if (base.data.containsKey(key)) { - IdentityHashMap, Object> newBaseData = new IdentityHashMap<>(base.data); - newBaseData.remove(key); - base = new Attributes(newBaseData); - } - if (newdata != null) { + if (base != null) { + if (base.data.containsKey(key)) { + data(0).remove(key); + } + } else { newdata.remove(key); } return this; @@ -264,11 +267,6 @@ public Builder setAll(Attributes other) { */ public Attributes build() { if (newdata != null) { - for (Map.Entry, Object> entry : base.data.entrySet()) { - if (!newdata.containsKey(entry.getKey())) { - newdata.put(entry.getKey(), entry.getValue()); - } - } base = new Attributes(newdata); newdata = null; }