Commit 66bfca85 by Abseil Team Committed by Copybara-Service

Auto increase inlined capacity whenever it does not affect class' size.

PiperOrigin-RevId: 487292771
Change-Id: I2454e28fe91017fb2954a4ad194712fcafe893d2
parent 64f00b1f
...@@ -661,10 +661,22 @@ class InlinedVector { ...@@ -661,10 +661,22 @@ class InlinedVector {
ABSL_HARDENING_ASSERT(pos <= end()); ABSL_HARDENING_ASSERT(pos <= end());
value_type dealias(std::forward<Args>(args)...); value_type dealias(std::forward<Args>(args)...);
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2
// It appears that GCC thinks that since `pos` is a const pointer and may
// point to uninitialized memory at this point, a warning should be
// issued. But `pos` is actually only used to compute an array index to
// write to.
#if !defined(__clang__) && defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
return storage_.Insert(pos, return storage_.Insert(pos,
IteratorValueAdapter<A, MoveIterator<A>>( IteratorValueAdapter<A, MoveIterator<A>>(
MoveIterator<A>(std::addressof(dealias))), MoveIterator<A>(std::addressof(dealias))),
1); 1);
#if !defined(__clang__) && defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
} }
// `InlinedVector::emplace_back(...)` // `InlinedVector::emplace_back(...)`
......
...@@ -1208,6 +1208,8 @@ TYPED_TEST_P(InstanceTest, CountConstructorsDestructorsOnMoveAssignment) { ...@@ -1208,6 +1208,8 @@ TYPED_TEST_P(InstanceTest, CountConstructorsDestructorsOnMoveAssignment) {
} }
TEST(CountElemAssign, SimpleTypeWithInlineBacking) { TEST(CountElemAssign, SimpleTypeWithInlineBacking) {
const size_t inlined_capacity = absl::InlinedVector<int, 2>().capacity();
for (size_t original_size = 0; original_size <= 5; ++original_size) { for (size_t original_size = 0; original_size <= 5; ++original_size) {
SCOPED_TRACE(original_size); SCOPED_TRACE(original_size);
// Original contents are [12345, 12345, ...] // Original contents are [12345, 12345, ...]
...@@ -1217,9 +1219,9 @@ TEST(CountElemAssign, SimpleTypeWithInlineBacking) { ...@@ -1217,9 +1219,9 @@ TEST(CountElemAssign, SimpleTypeWithInlineBacking) {
original_contents.end()); original_contents.end());
v.assign(2, 123); v.assign(2, 123);
EXPECT_THAT(v, AllOf(SizeIs(2u), ElementsAre(123, 123))); EXPECT_THAT(v, AllOf(SizeIs(2u), ElementsAre(123, 123)));
if (original_size <= 2) { if (original_size <= inlined_capacity) {
// If the original had inline backing, it should stay inline. // If the original had inline backing, it should stay inline.
EXPECT_EQ(2u, v.capacity()); EXPECT_EQ(v.capacity(), inlined_capacity);
} }
} }
} }
...@@ -1360,6 +1362,8 @@ TEST(RangedConstructor, ElementsAreConstructed) { ...@@ -1360,6 +1362,8 @@ TEST(RangedConstructor, ElementsAreConstructed) {
} }
TEST(RangedAssign, SimpleType) { TEST(RangedAssign, SimpleType) {
const size_t inlined_capacity = absl::InlinedVector<int, 3>().capacity();
// Test for all combinations of original sizes (empty and non-empty inline, // Test for all combinations of original sizes (empty and non-empty inline,
// and out of line) and target sizes. // and out of line) and target sizes.
for (size_t original_size = 0; original_size <= 5; ++original_size) { for (size_t original_size = 0; original_size <= 5; ++original_size) {
...@@ -1367,13 +1371,13 @@ TEST(RangedAssign, SimpleType) { ...@@ -1367,13 +1371,13 @@ TEST(RangedAssign, SimpleType) {
// Original contents are [12345, 12345, ...] // Original contents are [12345, 12345, ...]
std::vector<int> original_contents(original_size, 12345); std::vector<int> original_contents(original_size, 12345);
for (int target_size = 0; target_size <= 5; ++target_size) { for (size_t target_size = 0; target_size <= 5; ++target_size) {
SCOPED_TRACE(target_size); SCOPED_TRACE(target_size);
// New contents are [3, 4, ...] // New contents are [3, 4, ...]
std::vector<int> new_contents; std::vector<int> new_contents;
for (int i = 0; i < target_size; ++i) { for (size_t i = 0; i < target_size; ++i) {
new_contents.push_back(i + 3); new_contents.push_back(static_cast<int>(i + 3));
} }
absl::InlinedVector<int, 3> v(original_contents.begin(), absl::InlinedVector<int, 3> v(original_contents.begin(),
...@@ -1382,9 +1386,10 @@ TEST(RangedAssign, SimpleType) { ...@@ -1382,9 +1386,10 @@ TEST(RangedAssign, SimpleType) {
EXPECT_EQ(new_contents.size(), v.size()); EXPECT_EQ(new_contents.size(), v.size());
EXPECT_LE(new_contents.size(), v.capacity()); EXPECT_LE(new_contents.size(), v.capacity());
if (target_size <= 3 && original_size <= 3) { if (target_size <= inlined_capacity &&
original_size <= inlined_capacity) {
// Storage should stay inline when target size is small. // Storage should stay inline when target size is small.
EXPECT_EQ(3u, v.capacity()); EXPECT_EQ(v.capacity(), inlined_capacity);
} }
EXPECT_THAT(v, ElementsAreArray(new_contents)); EXPECT_THAT(v, ElementsAreArray(new_contents));
} }
...@@ -1470,9 +1475,12 @@ TEST(InitializerListConstructor, DisparateTypesInList) { ...@@ -1470,9 +1475,12 @@ TEST(InitializerListConstructor, DisparateTypesInList) {
} }
TEST(InitializerListConstructor, ComplexTypeWithInlineBacking) { TEST(InitializerListConstructor, ComplexTypeWithInlineBacking) {
EXPECT_THAT((absl::InlinedVector<CopyableMovableInstance, 1>{ const size_t inlined_capacity =
CopyableMovableInstance(0)}), absl::InlinedVector<CopyableMovableInstance, 1>().capacity();
AllOf(SizeIs(1u), CapacityIs(1u), ElementsAre(ValueIs(0)))); EXPECT_THAT(
(absl::InlinedVector<CopyableMovableInstance, 1>{
CopyableMovableInstance(0)}),
AllOf(SizeIs(1u), CapacityIs(inlined_capacity), ElementsAre(ValueIs(0))));
} }
TEST(InitializerListConstructor, ComplexTypeWithReallocationRequired) { TEST(InitializerListConstructor, ComplexTypeWithReallocationRequired) {
...@@ -2022,4 +2030,31 @@ TEST(NonSwappableSwapTest, SwapThis) { ...@@ -2022,4 +2030,31 @@ TEST(NonSwappableSwapTest, SwapThis) {
EXPECT_THAT(v, Pointwise(HasValue(), {1, 2, 3})); EXPECT_THAT(v, Pointwise(HasValue(), {1, 2, 3}));
} }
template <size_t N>
using CharVec = absl::InlinedVector<char, N>;
// Warning: This struct "simulates" the type `InlinedVector::Storage::Allocated`
// to make reasonable expectations for inlined storage capacity optimization. If
// implementation changes `Allocated`, then `MySpan` and tests that use it need
// to be updated accordingly.
template <typename T>
struct MySpan {
T* data;
size_t size;
};
TEST(StorageTest, InlinedCapacityAutoIncrease) {
// The requested capacity is auto increased to `sizeof(MySpan<char>)`.
EXPECT_GT(CharVec<1>().capacity(), 1);
EXPECT_EQ(CharVec<1>().capacity(), sizeof(MySpan<char>));
EXPECT_EQ(CharVec<1>().capacity(), CharVec<2>().capacity());
EXPECT_EQ(sizeof(CharVec<1>), sizeof(CharVec<2>));
// The requested capacity is auto increased to
// `sizeof(MySpan<int>) / sizeof(int)`.
EXPECT_GT((absl::InlinedVector<int, 1>().capacity()), 1);
EXPECT_EQ((absl::InlinedVector<int, 1>().capacity()),
sizeof(MySpan<int>) / sizeof(int));
}
} // anonymous namespace } // anonymous namespace
...@@ -379,7 +379,9 @@ class Storage { ...@@ -379,7 +379,9 @@ class Storage {
return data_.allocated.allocated_capacity; return data_.allocated.allocated_capacity;
} }
SizeType<A> GetInlinedCapacity() const { return static_cast<SizeType<A>>(N); } SizeType<A> GetInlinedCapacity() const {
return static_cast<SizeType<A>>(kOptimalInlinedSize);
}
StorageView<A> MakeStorageView() { StorageView<A> MakeStorageView() {
return GetIsAllocated() ? StorageView<A>{GetAllocatedData(), GetSize(), return GetIsAllocated() ? StorageView<A>{GetAllocatedData(), GetSize(),
...@@ -483,8 +485,15 @@ class Storage { ...@@ -483,8 +485,15 @@ class Storage {
SizeType<A> allocated_capacity; SizeType<A> allocated_capacity;
}; };
// `kOptimalInlinedSize` is an automatically adjusted inlined capacity of the
// `InlinedVector`. Sometimes, it is possible to increase the capacity (from
// the user requested `N`) without increasing the size of the `InlinedVector`.
static constexpr size_t kOptimalInlinedSize =
(std::max)(N, sizeof(Allocated) / sizeof(ValueType<A>));
struct Inlined { struct Inlined {
alignas(ValueType<A>) char inlined_data[sizeof(ValueType<A>[N])]; alignas(ValueType<A>) char inlined_data[sizeof(
ValueType<A>[kOptimalInlinedSize])];
}; };
union Data { union Data {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment