[PATCH] D90884: [SmallVector] Add a default small size.

Sean Silva via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Thu Nov 5 14:14:16 PST 2020


silvas created this revision.
Herald added subscribers: llvm-commits, dexonsmith.
Herald added a project: LLVM.
silvas requested review of this revision.

I find that I frequently set a fairly mindless small size (I tend to randomly choose "6").  This patch add a principled default small size to SmallVector.

The policy for the default small size is to guarantee that:

  sizeof(SmallVector<T>) <=
    kDefaultSmallSizeHeaderSizeMultiplier * sizeof(SmallVectorImpl<T>)

I have set `kDefaultSmallSizeHeaderSizeMultiplier == 3` based on a
spot-check of various SmallVector's in the codebase.
That is, `SmallVector<T>` will never be more than 3 vector headers.

This tends to match my intuition: depending on the element size, you
want to be careful about your small size -- basically we just want to
ensure not too much stack growth (or heap allocation growth for the
heap-allocated case). Also, looking back through llvm-dev, it looks
like Reid suggested something similar in
this thread <https://groups.google.com/g/llvm-dev/c/q1OyHZy8KVc/m/1l_AasOLBAAJ>
though his suggestion corresponds to
`kDefaultSmallSizeHeaderSizeMultiplier == 2` (happy to change that).

This default also tends to do the right thing in
`SmallVector<SmallVector<T>>` sorts of situations (also mentioned in
that thread as problematic). For these, the outer SmallVector will not
amplify the sizes of the inner SmallVectors. Of course, users can do
`SmallVector<SmallVector<T, 0>>` to indicate that the inner
SmallVector's should have no inline elements, and the outer SmallVector
then be able to fit some inner SmallVector's inline.

Finally, forgetting the small size is a frequent extra edit/rebuild
iteration for me. This avoids that which makes me more productive.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D90884

Files:
  llvm/include/llvm/ADT/SmallVector.h
  llvm/unittests/ADT/SmallVectorTest.cpp


Index: llvm/unittests/ADT/SmallVectorTest.cpp
===================================================================
--- llvm/unittests/ADT/SmallVectorTest.cpp
+++ llvm/unittests/ADT/SmallVectorTest.cpp
@@ -1029,4 +1029,29 @@
   EXPECT_TRUE(makeArrayRef(V2).equals({4, 5, 3, 2}));
 }
 
+struct BigElementType {
+  char Arr[10000] = {0};
+};
+typedef ::testing::Types<SmallVector<int8_t>, SmallVector<int16_t>,
+                         SmallVector<int32_t>, SmallVector<int64_t>,
+                         SmallVector<void *>, SmallVector<BigElementType>>
+    SmallVectorDefaultSmallSizeTestTypes;
+// Test fixture class
+template <typename VectorT>
+class SmallVectorDefaultSmallSizeTest : public testing::Test {
+protected:
+  VectorT theVector;
+};
+
+TYPED_TEST_CASE(SmallVectorDefaultSmallSizeTest,
+                SmallVectorDefaultSmallSizeTestTypes);
+
+TYPED_TEST(SmallVectorDefaultSmallSizeTest, DefaultSmallSize) {
+  using ValueT = typename decltype(this->theVector)::value_type;
+  static_assert(sizeof(this->theVector) <=
+                    kDefaultSmallSizeHeaderSizeMultiplier *
+                        sizeof(SmallVectorImpl<ValueT>),
+                "");
+}
+
 } // end namespace
Index: llvm/include/llvm/ADT/SmallVector.h
===================================================================
--- llvm/include/llvm/ADT/SmallVector.h
+++ llvm/include/llvm/ADT/SmallVector.h
@@ -892,6 +892,21 @@
 /// well-defined.
 template <typename T> struct alignas(T) SmallVectorStorage<T, 0> {};
 
+// Parameter controlling the default small size for SmallVector.
+//
+// We guarantee that a default-small-sized SmallVector has sizeof no more than
+// kDefaultSmallSizeHeaderSizeMultiplier times bigger than just the header.
+//
+// This prevents unexpected variation based on `sizeof(value_type)`.
+constexpr size_t kDefaultSmallSizeHeaderSizeMultiplier = 3;
+template <typename ElementTy> constexpr size_t calculateDefaultSmallSize() {
+  // Discount the size of the header itself when calculating the maximum inline
+  // bytes, so subtract one fromthe multiplier.
+  size_t MaxInlineBytes = (kDefaultSmallSizeHeaderSizeMultiplier - 1) *
+                          sizeof(SmallVectorImpl<ElementTy>);
+  return MaxInlineBytes / sizeof(ElementTy);
+}
+
 /// This is a 'vector' (really, a variable-sized array), optimized
 /// for the case when the array is small.  It contains some number of elements
 /// in-place, which allows it to avoid heap allocation when the actual number of
@@ -900,7 +915,7 @@
 ///
 /// Note that this does not attempt to be exception safe.
 ///
-template <typename T, unsigned N>
+template <typename T, unsigned N = calculateDefaultSmallSize<T>()>
 class LLVM_GSL_OWNER SmallVector : public SmallVectorImpl<T>,
                                    SmallVectorStorage<T, N> {
 public:


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D90884.303259.patch
Type: text/x-patch
Size: 2818 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20201105/5288e4de/attachment.bin>


More information about the llvm-commits mailing list