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 {
ABSL_HARDENING_ASSERT(pos <= end());
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,
IteratorValueAdapter<A, MoveIterator<A>>(
MoveIterator<A>(std::addressof(dealias))),
1);
#if !defined(__clang__) && defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
}
// `InlinedVector::emplace_back(...)`
......
......@@ -1208,6 +1208,8 @@ TYPED_TEST_P(InstanceTest, CountConstructorsDestructorsOnMoveAssignment) {
}
TEST(CountElemAssign, SimpleTypeWithInlineBacking) {
const size_t inlined_capacity = absl::InlinedVector<int, 2>().capacity();
for (size_t original_size = 0; original_size <= 5; ++original_size) {
SCOPED_TRACE(original_size);
// Original contents are [12345, 12345, ...]
......@@ -1217,9 +1219,9 @@ TEST(CountElemAssign, SimpleTypeWithInlineBacking) {
original_contents.end());
v.assign(2, 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.
EXPECT_EQ(2u, v.capacity());
EXPECT_EQ(v.capacity(), inlined_capacity);
}
}
}
......@@ -1360,6 +1362,8 @@ TEST(RangedConstructor, ElementsAreConstructed) {
}
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,
// and out of line) and target sizes.
for (size_t original_size = 0; original_size <= 5; ++original_size) {
......@@ -1367,13 +1371,13 @@ TEST(RangedAssign, SimpleType) {
// Original contents are [12345, 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);
// New contents are [3, 4, ...]
std::vector<int> new_contents;
for (int i = 0; i < target_size; ++i) {
new_contents.push_back(i + 3);
for (size_t i = 0; i < target_size; ++i) {
new_contents.push_back(static_cast<int>(i + 3));
}
absl::InlinedVector<int, 3> v(original_contents.begin(),
......@@ -1382,9 +1386,10 @@ TEST(RangedAssign, SimpleType) {
EXPECT_EQ(new_contents.size(), v.size());
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.
EXPECT_EQ(3u, v.capacity());
EXPECT_EQ(v.capacity(), inlined_capacity);
}
EXPECT_THAT(v, ElementsAreArray(new_contents));
}
......@@ -1470,9 +1475,12 @@ TEST(InitializerListConstructor, DisparateTypesInList) {
}
TEST(InitializerListConstructor, ComplexTypeWithInlineBacking) {
EXPECT_THAT((absl::InlinedVector<CopyableMovableInstance, 1>{
const size_t inlined_capacity =
absl::InlinedVector<CopyableMovableInstance, 1>().capacity();
EXPECT_THAT(
(absl::InlinedVector<CopyableMovableInstance, 1>{
CopyableMovableInstance(0)}),
AllOf(SizeIs(1u), CapacityIs(1u), ElementsAre(ValueIs(0))));
AllOf(SizeIs(1u), CapacityIs(inlined_capacity), ElementsAre(ValueIs(0))));
}
TEST(InitializerListConstructor, ComplexTypeWithReallocationRequired) {
......@@ -2022,4 +2030,31 @@ TEST(NonSwappableSwapTest, SwapThis) {
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
......@@ -379,7 +379,9 @@ class Storage {
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() {
return GetIsAllocated() ? StorageView<A>{GetAllocatedData(), GetSize(),
......@@ -483,8 +485,15 @@ class Storage {
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 {
alignas(ValueType<A>) char inlined_data[sizeof(ValueType<A>[N])];
alignas(ValueType<A>) char inlined_data[sizeof(
ValueType<A>[kOptimalInlinedSize])];
};
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