summaryrefslogtreecommitdiff
path: root/absl/container
diff options
context:
space:
mode:
Diffstat (limited to 'absl/container')
-rw-r--r--absl/container/inlined_vector.h8
-rw-r--r--absl/container/inlined_vector_benchmark.cc22
-rw-r--r--absl/container/internal/inlined_vector.h66
3 files changed, 90 insertions, 6 deletions
diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h
index 90bb96e8..7c182342 100644
--- a/absl/container/inlined_vector.h
+++ b/absl/container/inlined_vector.h
@@ -167,11 +167,13 @@ class InlinedVector {
// Creates an inlined vector by copying the contents of `other` using `alloc`.
InlinedVector(const InlinedVector& other, const allocator_type& alloc)
: storage_(alloc) {
- if (IsMemcpyOk::value && !other.storage_.GetIsAllocated()) {
+ if (other.empty()) {
+ // Empty; nothing to do.
+ } else if (IsMemcpyOk::value && !other.storage_.GetIsAllocated()) {
+ // Memcpy-able and do not need allocation.
storage_.MemcpyFrom(other.storage_);
} else {
- storage_.Initialize(IteratorValueAdapter<const_pointer>(other.data()),
- other.size());
+ storage_.InitFrom(other.storage_);
}
}
diff --git a/absl/container/inlined_vector_benchmark.cc b/absl/container/inlined_vector_benchmark.cc
index b8dafe93..e256fad6 100644
--- a/absl/container/inlined_vector_benchmark.cc
+++ b/absl/container/inlined_vector_benchmark.cc
@@ -534,6 +534,28 @@ void BM_ConstructFromMove(benchmark::State& state) {
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromMove, TrivialType);
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromMove, NontrivialType);
+// Measure cost of copy-constructor+destructor.
+void BM_CopyTrivial(benchmark::State& state) {
+ const int n = state.range(0);
+ InlVec<int64_t> src(n);
+ for (auto s : state) {
+ InlVec<int64_t> copy(src);
+ benchmark::DoNotOptimize(copy);
+ }
+}
+BENCHMARK(BM_CopyTrivial)->Arg(0)->Arg(1)->Arg(kLargeSize);
+
+// Measure cost of copy-constructor+destructor.
+void BM_CopyNonTrivial(benchmark::State& state) {
+ const int n = state.range(0);
+ InlVec<InlVec<int64_t>> src(n);
+ for (auto s : state) {
+ InlVec<InlVec<int64_t>> copy(src);
+ benchmark::DoNotOptimize(copy);
+ }
+}
+BENCHMARK(BM_CopyNonTrivial)->Arg(0)->Arg(1)->Arg(kLargeSize);
+
template <typename T, size_t FromSize, size_t ToSize>
void BM_AssignSizeRef(benchmark::State& state) {
auto size = ToSize;
diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h
index 120849cd..b8aec45b 100644
--- a/absl/container/internal/inlined_vector.h
+++ b/absl/container/internal/inlined_vector.h
@@ -81,6 +81,23 @@ void DestroyElements(AllocatorType* alloc_ptr, Pointer destroy_first,
}
}
+// If kUseMemcpy is true, memcpy(dst, src, n); else do nothing.
+// Useful to avoid compiler warnings when memcpy() is used for T values
+// that are not trivially copyable in non-reachable code.
+template <bool kUseMemcpy>
+inline void MemcpyIfAllowed(void* dst, const void* src, size_t n);
+
+// memcpy when allowed.
+template <>
+inline void MemcpyIfAllowed<true>(void* dst, const void* src, size_t n) {
+ memcpy(dst, src, n);
+}
+
+// Do nothing for types that are not memcpy-able. This function is only
+// called from non-reachable branches.
+template <>
+inline void MemcpyIfAllowed<false>(void*, const void*, size_t) {}
+
template <typename AllocatorType, typename Pointer, typename ValueAdapter,
typename SizeType>
void ConstructElements(AllocatorType* alloc_ptr, Pointer construct_first,
@@ -310,9 +327,14 @@ class Storage {
: metadata_(alloc, /* size and is_allocated */ 0) {}
~Storage() {
- pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData();
- inlined_vector_internal::DestroyElements(GetAllocPtr(), data, GetSize());
- DeallocateIfAllocated();
+ if (GetSizeAndIsAllocated() == 0) {
+ // Empty and not allocated; nothing to do.
+ } else if (IsMemcpyOk::value) {
+ // No destructors need to be run; just deallocate if necessary.
+ DeallocateIfAllocated();
+ } else {
+ DestroyContents();
+ }
}
// ---------------------------------------------------------------------------
@@ -370,6 +392,8 @@ class Storage {
// Storage Member Mutators
// ---------------------------------------------------------------------------
+ ABSL_ATTRIBUTE_NOINLINE void InitFrom(const Storage& other);
+
template <typename ValueAdapter>
void Initialize(ValueAdapter values, size_type new_size);
@@ -452,6 +476,8 @@ class Storage {
}
private:
+ ABSL_ATTRIBUTE_NOINLINE void DestroyContents();
+
using Metadata =
container_internal::CompressedTuple<allocator_type, size_type>;
@@ -477,6 +503,40 @@ class Storage {
};
template <typename T, size_t N, typename A>
+void Storage<T, N, A>::DestroyContents() {
+ pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData();
+ inlined_vector_internal::DestroyElements(GetAllocPtr(), data, GetSize());
+ DeallocateIfAllocated();
+}
+
+template <typename T, size_t N, typename A>
+void Storage<T, N, A>::InitFrom(const Storage& other) {
+ const auto n = other.GetSize();
+ assert(n > 0); // Empty sources handled handled in caller.
+ const_pointer src;
+ pointer dst;
+ if (!other.GetIsAllocated()) {
+ dst = GetInlinedData();
+ src = other.GetInlinedData();
+ } else {
+ // Because this is only called from the `InlinedVector` constructors, it's
+ // safe to take on the allocation with size `0`. If `ConstructElements(...)`
+ // throws, deallocation will be automatically handled by `~Storage()`.
+ size_type new_capacity = ComputeCapacity(GetInlinedCapacity(), n);
+ dst = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity);
+ SetAllocatedData(dst, new_capacity);
+ src = other.GetAllocatedData();
+ }
+ if (IsMemcpyOk::value) {
+ MemcpyIfAllowed<IsMemcpyOk::value>(dst, src, sizeof(dst[0]) * n);
+ } else {
+ auto values = IteratorValueAdapter<const_pointer>(src);
+ inlined_vector_internal::ConstructElements(GetAllocPtr(), dst, &values, n);
+ }
+ GetSizeAndIsAllocated() = other.GetSizeAndIsAllocated();
+}
+
+template <typename T, size_t N, typename A>
template <typename ValueAdapter>
auto Storage<T, N, A>::Initialize(ValueAdapter values, size_type new_size)
-> void {