Skip to content

Commit fa0ff49

Browse files
fmeumcopybara-github
authored andcommitted
Intern empty Depsets
Empty `Depset`s are interned per order, which decreases allocations as well as retained heap when a `Depset` is stored in a `struct` in a provider (providers with schema already unwrap most `Depset`s). Fixes #19380 Closes #19387. PiperOrigin-RevId: 563104923 Change-Id: If66fb4a108ef7569a4f95e7af3a74ac84d1c4636
1 parent 2067a8b commit fa0ff49

File tree

3 files changed

+30
-10
lines changed

3 files changed

+30
-10
lines changed

src/main/java/com/google/devtools/build/lib/collect/nestedset/Depset.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public final class Depset implements StarlarkValue, Debug.ValueWithDebugAttribut
102102
@Nullable private final Class<?> elemClass;
103103
private final NestedSet<?> set;
104104

105-
private Depset(@Nullable Class<?> elemClass, NestedSet<?> set) {
105+
Depset(@Nullable Class<?> elemClass, NestedSet<?> set) {
106106
this.elemClass = elemClass;
107107
this.set = set;
108108
}
@@ -145,9 +145,10 @@ private static void checkElement(Object x, boolean strict) throws EvalException
145145
// Object.class means "any Starlark value" but in fact allows any Java value.
146146
public static <T> Depset of(Class<T> elemClass, NestedSet<T> set) {
147147
Preconditions.checkNotNull(elemClass, "elemClass cannot be null");
148-
// Having a shared EMPTY_DEPSET instance, could reduce working heap. Empty depsets are not
149-
// retained though, because of optimization in StarlarkProvider.optimizeField.
150-
return new Depset(set.isEmpty() ? null : ElementType.getTypeClass(elemClass), set);
148+
if (set.isEmpty()) {
149+
return set.getOrder().emptyDepset();
150+
}
151+
return new Depset(ElementType.getTypeClass(elemClass), set);
151152
}
152153

153154
/**
@@ -259,15 +260,11 @@ public static <T> NestedSet<T> cast(Object x, Class<T> type, String what) throws
259260
public static <T> NestedSet<T> noneableCast(Object x, Class<T> type, String what)
260261
throws EvalException {
261262
if (x == Starlark.NONE) {
262-
@SuppressWarnings("unchecked")
263-
NestedSet<T> empty = (NestedSet<T>) EMPTY;
264-
return empty;
263+
return NestedSetBuilder.emptySet(Order.STABLE_ORDER);
265264
}
266265
return cast(x, type, what);
267266
}
268267

269-
private static final NestedSet<?> EMPTY = NestedSetBuilder.<Object>emptySet(Order.STABLE_ORDER);
270-
271268
public boolean isEmpty() {
272269
return set.isEmpty();
273270
}
@@ -377,6 +374,9 @@ public static Depset fromDirectAndTransitive(
377374
}
378375
}
379376

377+
if (builder.isEmpty()) {
378+
return builder.getOrder().emptyDepset();
379+
}
380380
return new Depset(type, builder.build());
381381
}
382382

src/main/java/com/google/devtools/build/lib/collect/nestedset/Order.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,12 @@ public enum Order {
112112

113113
private final String starlarkName;
114114
private final NestedSet<?> emptySet;
115+
private final Depset emptyDepset;
115116

116-
private Order(String starlarkName) {
117+
Order(String starlarkName) {
117118
this.starlarkName = starlarkName;
118119
this.emptySet = new NestedSet<>(this);
120+
this.emptyDepset = new Depset(null, this.emptySet);
119121
}
120122

121123
@SerializationConstant @AutoCodec.VisibleForSerialization
@@ -150,6 +152,11 @@ <E> NestedSet<E> emptySet() {
150152
return (NestedSet<E>) emptySet;
151153
}
152154

155+
/** Returns an empty depset of the given ordering. */
156+
Depset emptyDepset() {
157+
return emptyDepset;
158+
}
159+
153160
public String getStarlarkName() {
154161
return starlarkName;
155162
}

src/test/java/com/google/devtools/build/lib/collect/nestedset/DepsetTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,4 +406,17 @@ ev.new Scenario()
406406
+ " NoneType'",
407407
"depset(direct='hello')");
408408
}
409+
410+
@Test
411+
public void testEmptyDepsetInternedPerOrder() throws Exception {
412+
ev.exec(
413+
"stable1 = depset()",
414+
"stable2 = depset()",
415+
"preorder1 = depset(order = 'preorder')",
416+
"preorder2 = depset(order = 'preorder')");
417+
assertThat(ev.lookup("stable1")).isSameInstanceAs(ev.lookup("stable2"));
418+
assertThat(ev.lookup("preorder1")).isSameInstanceAs(ev.lookup("preorder2"));
419+
assertThat(ev.lookup("stable1")).isNotSameInstanceAs(ev.lookup("preorder1"));
420+
assertThat(ev.lookup("stable2")).isNotSameInstanceAs(ev.lookup("preorder2"));
421+
}
409422
}

0 commit comments

Comments
 (0)