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

PiperOrigin-RevId: 487292771
Change-Id: I2454e28fe91017fb2954a4ad194712fcafe893d2
pull/1272/merge
Abseil Team 2 years ago committed by Copybara-Service
parent 64f00b1f4a
commit 66bfca85c8
  1. 12
      absl/container/inlined_vector.h
  2. 55
      absl/container/inlined_vector_test.cc
  3. 13
      absl/container/internal/inlined_vector.h

@ -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>{
CopyableMovableInstance(0)}),
AllOf(SizeIs(1u), CapacityIs(1u), ElementsAre(ValueIs(0))));
const size_t inlined_capacity =
absl::InlinedVector<CopyableMovableInstance, 1>().capacity();
EXPECT_THAT(
(absl::InlinedVector<CopyableMovableInstance, 1>{
CopyableMovableInstance(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 {

Loading…
Cancel
Save