Skip to content

Commit f2c268f

Browse files
authored
[ImmutableSet] Optimize add/remove operations to avoid redundant tree modifications (#159845)
Optimize ImmutableSet operations to avoid unnecessary tree modifications when adding existing elements or removing non-existent elements. - Modified `ImutAVLFactory::add_internal()` to return the original tree when both key and value are the same, avoiding unnecessary node creation - Updated `ImutAVLFactory::remove_internal()` and `add_internal()` to return the original tree when no changes are made. Note that `balanceTree` always end up creating at least one node even when no rebalancing is done. So we also need to avoid unnecessary calls to it.
1 parent b5c0102 commit f2c268f

File tree

3 files changed

+70
-22
lines changed

3 files changed

+70
-22
lines changed

clang/lib/Analysis/LifetimeSafety.cpp

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -910,13 +910,10 @@ template <typename T>
910910
static llvm::ImmutableSet<T> join(llvm::ImmutableSet<T> A,
911911
llvm::ImmutableSet<T> B,
912912
typename llvm::ImmutableSet<T>::Factory &F) {
913-
if (A == B)
914-
return A;
915913
if (A.getHeight() < B.getHeight())
916914
std::swap(A, B);
917915
for (const T &E : B)
918-
if (!A.contains(E))
919-
A = F.add(A, E);
916+
A = F.add(A, E);
920917
return A;
921918
}
922919

@@ -950,11 +947,10 @@ join(llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> B,
950947
for (const auto &Entry : B) {
951948
const K &Key = Entry.first;
952949
const V &ValB = Entry.second;
953-
const V *ValA = A.lookup(Key);
954-
if (!ValA)
955-
A = F.add(A, Key, ValB);
956-
else if (*ValA != ValB)
950+
if (const V *ValA = A.lookup(Key))
957951
A = F.add(A, Key, JoinValues(*ValA, ValB));
952+
else
953+
A = F.add(A, Key, ValB);
958954
}
959955
return A;
960956
}

llvm/include/llvm/ADT/ImmutableSet.h

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -531,43 +531,64 @@ class ImutAVLFactory {
531531
/// add_internal - Creates a new tree that includes the specified
532532
/// data and the data from the original tree. If the original tree
533533
/// already contained the data item, the original tree is returned.
534-
TreeTy* add_internal(value_type_ref V, TreeTy* T) {
534+
TreeTy *add_internal(value_type_ref V, TreeTy *T) {
535535
if (isEmpty(T))
536536
return createNode(T, V, T);
537537
assert(!T->isMutable());
538538

539539
key_type_ref K = ImutInfo::KeyOfValue(V);
540540
key_type_ref KCurrent = ImutInfo::KeyOfValue(getValue(T));
541541

542-
if (ImutInfo::isEqual(K,KCurrent))
542+
if (ImutInfo::isEqual(K, KCurrent)) {
543+
// If both key and value are same, return the original tree.
544+
if (ImutInfo::isDataEqual(ImutInfo::DataOfValue(V),
545+
ImutInfo::DataOfValue(getValue(T))))
546+
return T;
547+
// Otherwise create a new node with the new value.
543548
return createNode(getLeft(T), V, getRight(T));
544-
else if (ImutInfo::isLess(K,KCurrent))
545-
return balanceTree(add_internal(V, getLeft(T)), getValue(T), getRight(T));
549+
}
550+
551+
TreeTy *NewL = getLeft(T);
552+
TreeTy *NewR = getRight(T);
553+
if (ImutInfo::isLess(K, KCurrent))
554+
NewL = add_internal(V, NewL);
546555
else
547-
return balanceTree(getLeft(T), getValue(T), add_internal(V, getRight(T)));
556+
NewR = add_internal(V, NewR);
557+
558+
// If no changes were made, return the original tree. Otherwise, balance the
559+
// tree and return the new root.
560+
return NewL == getLeft(T) && NewR == getRight(T)
561+
? T
562+
: balanceTree(NewL, getValue(T), NewR);
548563
}
549564

550565
/// remove_internal - Creates a new tree that includes all the data
551566
/// from the original tree except the specified data. If the
552567
/// specified data did not exist in the original tree, the original
553568
/// tree is returned.
554-
TreeTy* remove_internal(key_type_ref K, TreeTy* T) {
569+
TreeTy *remove_internal(key_type_ref K, TreeTy *T) {
555570
if (isEmpty(T))
556571
return T;
557572

558573
assert(!T->isMutable());
559574

560575
key_type_ref KCurrent = ImutInfo::KeyOfValue(getValue(T));
561576

562-
if (ImutInfo::isEqual(K,KCurrent)) {
577+
if (ImutInfo::isEqual(K, KCurrent))
563578
return combineTrees(getLeft(T), getRight(T));
564-
} else if (ImutInfo::isLess(K,KCurrent)) {
565-
return balanceTree(remove_internal(K, getLeft(T)),
566-
getValue(T), getRight(T));
567-
} else {
568-
return balanceTree(getLeft(T), getValue(T),
569-
remove_internal(K, getRight(T)));
570-
}
579+
580+
TreeTy *NewL = getLeft(T);
581+
TreeTy *NewR = getRight(T);
582+
if (ImutInfo::isLess(K, KCurrent))
583+
NewL = remove_internal(K, NewL);
584+
else
585+
NewR = remove_internal(K, NewR);
586+
587+
// If no changes were made, return the original tree. Otherwise, balance the
588+
// tree and return the new root.
589+
return NewL == getLeft(T) && NewR == getRight(T)
590+
? T
591+
: balanceTree(NewL, getValue(T), NewR);
571592
}
572593

573594
TreeTy* combineTrees(TreeTy* L, TreeTy* R) {

llvm/unittests/ADT/ImmutableSetTest.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,35 @@ TEST_F(ImmutableSetTest, IterLongSetTest) {
164164
ASSERT_EQ(6, i);
165165
}
166166

167+
TEST_F(ImmutableSetTest, AddIfNotFoundTest) {
168+
ImmutableSet<long>::Factory f(/*canonicalize=*/false);
169+
ImmutableSet<long> S = f.getEmptySet();
170+
S = f.add(S, 1);
171+
S = f.add(S, 2);
172+
S = f.add(S, 3);
173+
174+
ImmutableSet<long> T1 = f.add(S, 1);
175+
ImmutableSet<long> T2 = f.add(S, 2);
176+
ImmutableSet<long> T3 = f.add(S, 3);
177+
EXPECT_EQ(S.getRoot(), T1.getRoot());
178+
EXPECT_EQ(S.getRoot(), T2.getRoot());
179+
EXPECT_EQ(S.getRoot(), T3.getRoot());
180+
181+
ImmutableSet<long> U = f.add(S, 4);
182+
EXPECT_NE(S.getRoot(), U.getRoot());
183+
}
184+
185+
TEST_F(ImmutableSetTest, RemoveIfNotFoundTest) {
186+
ImmutableSet<long>::Factory f(/*canonicalize=*/false);
187+
ImmutableSet<long> S = f.getEmptySet();
188+
S = f.add(S, 1);
189+
S = f.add(S, 2);
190+
S = f.add(S, 3);
191+
192+
ImmutableSet<long> T = f.remove(S, 4);
193+
EXPECT_EQ(S.getRoot(), T.getRoot());
194+
195+
ImmutableSet<long> U = f.remove(S, 3);
196+
EXPECT_NE(S.getRoot(), U.getRoot());
167197
}
198+
} // namespace

0 commit comments

Comments
 (0)