Skip to content

Commit 826048e

Browse files
committed
inlined_vector: Use trivial relocation for erase
Trivial relocatability also requires that the type not do anything weird with its assignment operator; update the type-trait to reflect this. (This is the definition used by BSL, Folly, HPX, Thrust, Parlay, Amadeus, and P1144.) Clang's builtin currently fails to check the assignment operator, but Abseil's type trait already refuses to use Clang's builtin. I'm working on getting that fixed in Clang. The reason this is important is that we want to be able to use memcpy to speedily relocate elements also in vector::erase. Implement that optimization for `absl::inlined_vector`. Prior art for the `vector::erase` optimization: https://github.com/bloomberg/bde/blob/e15f05be6/groups/bsl/bslalg/bslalg_arrayprimitives.h#L3787-L3799 https://github.com/facebook/folly/blob/main/folly/FBVector.h#L1254 https://github.com/qt/qtbase/blob/fbfee2d/src/corelib/tools/qarraydataops.h#L856-L861
1 parent 4358cb2 commit 826048e

File tree

2 files changed

+25
-12
lines changed

2 files changed

+25
-12
lines changed

absl/container/internal/inlined_vector.h

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -894,16 +894,30 @@ auto Storage<T, N, A>::Erase(ConstIterator<A> from, ConstIterator<A> to)
894894
std::distance(ConstIterator<A>(storage_view.data), from));
895895
SizeType<A> erase_end_index = erase_index + erase_size;
896896

897-
IteratorValueAdapter<A, MoveIterator<A>> move_values(
898-
MoveIterator<A>(storage_view.data + erase_end_index));
899-
900-
AssignElements<A>(storage_view.data + erase_index, move_values,
901-
storage_view.size - erase_end_index);
897+
// Fast path: if the value type is trivially relocatable and we know
898+
// the allocator doesn't do anything fancy, then we know it is legal for us to
899+
// simply destroy the elements in the "erasure window" (which cannot throw)
900+
// and then memcpy downward to close the window.
901+
if (absl::is_trivially_relocatable<value_type>::value &&
902+
std::is_nothrow_destructible<value_type>::value &&
903+
std::is_same<A, std::allocator<T>>::value) {
904+
DestroyAdapter<A>::DestroyElements(
905+
GetAllocator(), storage_view.data + erase_index,
906+
erase_size);
907+
std::memmove(reinterpret_cast<char*>(storage_view.data + erase_index),
908+
reinterpret_cast<const char*>(storage_view.data + erase_end_index),
909+
storage_view.size - erase_end_index);
910+
} else {
911+
IteratorValueAdapter<A, MoveIterator<A>> move_values(
912+
MoveIterator<A>(storage_view.data + erase_end_index));
902913

903-
DestroyAdapter<A>::DestroyElements(
904-
GetAllocator(), storage_view.data + (storage_view.size - erase_size),
905-
erase_size);
914+
AssignElements<A>(storage_view.data + erase_index, move_values,
915+
storage_view.size - erase_end_index);
906916

917+
DestroyAdapter<A>::DestroyElements(
918+
GetAllocator(), storage_view.data + (storage_view.size - erase_size),
919+
erase_size);
920+
}
907921
SubtractSize(erase_size);
908922
return Iterator<A>(storage_view.data + erase_index);
909923
}

absl/meta/type_traits.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -519,12 +519,11 @@ struct is_trivially_relocatable
519519
: std::integral_constant<bool, __is_trivially_relocatable(T)> {};
520520
#else
521521
// Otherwise we use a fallback that detects only those types we can feasibly
522-
// detect. Any time that has trivial move-construction and destruction
523-
// operations is by definition trivially relocatable.
522+
// detect. Any type that is trivially copyable is by definition trivially
523+
// relocatable.
524524
template <class T>
525525
struct is_trivially_relocatable
526-
: absl::conjunction<absl::is_trivially_move_constructible<T>,
527-
absl::is_trivially_destructible<T>> {};
526+
: absl::is_trivially_copyable<T> {};
528527
#endif
529528

530529
// absl::is_constant_evaluated()

0 commit comments

Comments
 (0)