[ADT] Add a reserve() method to DenseSet as well as an insert() for R-value
authorMehdi Amini <mehdi.amini@apple.com>
Sat, 13 Aug 2016 20:42:19 +0000 (20:42 +0000)
committerMehdi Amini <mehdi.amini@apple.com>
Sat, 13 Aug 2016 20:42:19 +0000 (20:42 +0000)
Recommit 278600 with some fixes to make the test more robust.

llvm-svn: 278604

llvm/include/llvm/ADT/DenseSet.h
llvm/unittests/ADT/DenseSetTest.cpp

index 3724a09..ddded85 100644 (file)
@@ -58,6 +58,10 @@ public:
   /// the Size of the set.
   void resize(size_t Size) { TheMap.resize(Size); }
 
+  /// Grow the DenseSet so that it can contain at least \p NumEntries items
+  /// before resizing again.
+  void reserve(size_t Size) { TheMap.reserve(Size); }
+
   void clear() {
     TheMap.clear();
   }
@@ -151,7 +155,12 @@ public:
 
   std::pair<iterator, bool> insert(const ValueT &V) {
     detail::DenseSetEmpty Empty;
-    return TheMap.insert(std::make_pair(V, Empty));
+    return TheMap.try_emplace(V, Empty);
+  }
+
+  std::pair<iterator, bool> insert(ValueT &&V) {
+    detail::DenseSetEmpty Empty;
+    return TheMap.try_emplace(std::move(V), Empty);
   }
 
   /// Alternative version of insert that uses a different (and possibly less
index 5952353..72af21e 100644 (file)
@@ -65,4 +65,75 @@ TEST(DenseSetCustomTest, FindAsTest) {
   EXPECT_TRUE(set.find_as("d") == set.end());
 }
 
+// Simple class that counts how many moves and copy happens when growing a map
+struct CountCopyAndMove {
+  static int Move;
+  static int Copy;
+  int Value;
+  CountCopyAndMove(int Value) : Value(Value) {}
+
+  CountCopyAndMove(const CountCopyAndMove &RHS) {
+    Value = RHS.Value;
+    Copy++;
+  }
+  CountCopyAndMove &operator=(const CountCopyAndMove &RHS) {
+    Value = RHS.Value;
+    Copy++;
+    return *this;
+  }
+  CountCopyAndMove(CountCopyAndMove &&RHS) {
+    Value = RHS.Value;
+    Move++;
+  }
+  CountCopyAndMove &operator=(const CountCopyAndMove &&RHS) {
+    Value = RHS.Value;
+    Move++;
+    return *this;
+  }
+};
+int CountCopyAndMove::Copy = 0;
+int CountCopyAndMove::Move = 0;
+} // anonymous namespace
+
+namespace llvm {
+// Specialization required to insert a CountCopyAndMove into a DenseSet.
+template <> struct DenseMapInfo<CountCopyAndMove> {
+  static inline CountCopyAndMove getEmptyKey() { return CountCopyAndMove(-1); };
+  static inline CountCopyAndMove getTombstoneKey() {
+    return CountCopyAndMove(-2);
+  };
+  static unsigned getHashValue(const CountCopyAndMove &Val) {
+    return Val.Value;
+  }
+  static bool isEqual(const CountCopyAndMove &LHS,
+                      const CountCopyAndMove &RHS) {
+    return LHS.Value == RHS.Value;
+  }
+};
+}
+
+namespace {
+// Make sure reserve actually gives us enough buckets to insert N items
+// without increasing allocation size.
+TEST(DenseSetCustomTest, ReserveTest) {
+  // Test a few different size, 48 is *not* a random choice: we need a value
+  // that is 2/3 of a power of two to stress the grow() condition, and the power
+  // of two has to be at least 64 because of minimum size allocation in the
+  // DenseMa. 66 is a value just above the 64 default init.
+  for (auto Size : {1, 2, 48, 66}) {
+    DenseSet<CountCopyAndMove> Set;
+    Set.reserve(Size);
+    unsigned MemorySize = Set.getMemorySize();
+    CountCopyAndMove::Copy = 0;
+    CountCopyAndMove::Move = 0;
+    for (int i = 0; i < Size; ++i)
+      Set.insert(CountCopyAndMove(i));
+    // Check that we didn't grow
+    EXPECT_EQ(MemorySize, Set.getMemorySize());
+    // Check that move was called the expected number of times
+    EXPECT_EQ(Size, CountCopyAndMove::Move);
+    // Check that no copy occured
+    EXPECT_EQ(0, CountCopyAndMove::Copy);
+  }
+}
 }