Skip to content

[ADT] Use perfect forwarding in SmallSet::insert() #108590

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 25, 2024

Conversation

vhscampos
Copy link
Member

Previously this method took arguments by const-ref. This patch changes the implementation to take perfectly forwarded arguments in the form of a universal reference. Now, the insertion method will take advantage of arguments passed as rvalue, potentially leading to performance improvements.

@llvmbot
Copy link
Member

llvmbot commented Sep 13, 2024

@llvm/pr-subscribers-llvm-adt

Author: Victor Campos (vhscampos)

Changes

Previously this method took arguments by const-ref. This patch changes the implementation to take perfectly forwarded arguments in the form of a universal reference. Now, the insertion method will take advantage of arguments passed as rvalue, potentially leading to performance improvements.


Full diff: https://github.com/llvm/llvm-project/pull/108590.diff

1 Files Affected:

  • (modified) llvm/include/llvm/ADT/SmallSet.h (+28-19)
diff --git a/llvm/include/llvm/ADT/SmallSet.h b/llvm/include/llvm/ADT/SmallSet.h
index d5f64e4f20f854..c8641111eda8f8 100644
--- a/llvm/include/llvm/ADT/SmallSet.h
+++ b/llvm/include/llvm/ADT/SmallSet.h
@@ -165,26 +165,10 @@ class SmallSet {
   /// Returns a pair. The first value of it is an iterator to the inserted
   /// element or the existing element in the set. The second value is true
   /// if the element is inserted (it was not in the set before).
-  std::pair<const_iterator, bool> insert(const T &V) {
-    if (!isSmall()) {
-      auto [I, Inserted] = Set.insert(V);
-      return std::make_pair(const_iterator(I), Inserted);
-    }
+  std::pair<const_iterator, bool> insert(const T &V) { return insertImpl(V); }
 
-    auto I = llvm::find(Vector, V);
-    if (I != Vector.end())    // Don't reinsert if it already exists.
-      return std::make_pair(const_iterator(I), false);
-    if (Vector.size() < N) {
-      Vector.push_back(V);
-      return std::make_pair(const_iterator(std::prev(Vector.end())), true);
-    }
-
-    // Otherwise, grow from vector to set.
-    while (!Vector.empty()) {
-      Set.insert(Vector.back());
-      Vector.pop_back();
-    }
-    return std::make_pair(const_iterator(Set.insert(V).first), true);
+  std::pair<const_iterator, bool> insert(T &&V) {
+    return insertImpl(std::move(V));
   }
 
   template <typename IterT>
@@ -231,6 +215,31 @@ class SmallSet {
 
 private:
   bool isSmall() const { return Set.empty(); }
+
+  template <typename ArgType>
+  std::pair<const_iterator, bool> insertImpl(ArgType &&V) {
+    static_assert(std::is_convertible_v<ArgType, T>,
+                  "ArgType must be convertible to T!");
+    if (!isSmall()) {
+      auto [I, Inserted] = Set.insert(std::forward<ArgType>(V));
+      return std::make_pair(const_iterator(I), Inserted);
+    }
+
+    auto I = llvm::find(Vector, V);
+    if (I != Vector.end()) // Don't reinsert if it already exists.
+      return std::make_pair(const_iterator(I), false);
+    if (Vector.size() < N) {
+      Vector.push_back(std::forward<ArgType>(V));
+      return std::make_pair(const_iterator(std::prev(Vector.end())), true);
+    }
+
+    // Otherwise, grow from vector to set.
+    Set.insert(std::make_move_iterator(Vector.begin()),
+               std::make_move_iterator(Vector.end()));
+    Vector.clear();
+    return std::make_pair(
+        const_iterator(Set.insert(std::forward<ArgType>(V)).first), true);
+  }
 };
 
 /// If this set is of pointer values, transparently switch over to using

Copy link
Member

@kuhar kuhar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a test showing the values are forwarded?

@vhscampos vhscampos changed the base branch from users/vhscampos/smallset-range-based-functions to users/vhscampos/smallset-simplify September 20, 2024 12:19
@vhscampos vhscampos force-pushed the users/vhscampos/smallset-simplify branch from 0b48b3b to 3877c99 Compare September 24, 2024 09:39
@vhscampos vhscampos force-pushed the users/vhscampos/smallset-insert-perfect-forwarding branch from e9587c0 to 8a9e20e Compare September 24, 2024 09:39
Base automatically changed from users/vhscampos/smallset-simplify to main September 25, 2024 10:24
An error occurred while trying to automatically change base from users/vhscampos/smallset-simplify to main September 25, 2024 10:24
Previously this method took arguments by const-ref. This patch changes
the implementation to take perfectly forwarded arguments in the form of
a universal reference. Now, the insertion method will take advantage of
arguments passed as rvalue, potentially leading to performance
improvements.
@vhscampos vhscampos force-pushed the users/vhscampos/smallset-insert-perfect-forwarding branch from 8a9e20e to 8605d54 Compare September 25, 2024 10:27
@vhscampos vhscampos merged commit 2a29d24 into main Sep 25, 2024
8 checks passed
@vhscampos vhscampos deleted the users/vhscampos/smallset-insert-perfect-forwarding branch September 25, 2024 11:43
@nikic
Copy link
Contributor

nikic commented Sep 25, 2024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants