Skip to content

Commit 078be5e

Browse files
committed
[WIP][ADT] Avoid slow size queries on append
Only calculate the size and reserve upfront when the input iterators support cheap (O(1)) distance. This is a follow up to: #136066 (comment).
1 parent c62afbf commit 078be5e

File tree

1 file changed

+48
-13
lines changed

1 file changed

+48
-13
lines changed

llvm/include/llvm/ADT/SmallVector.h

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,18 @@ template <typename T> class ArrayRef;
3636

3737
template <typename IteratorT> class iterator_range;
3838

39-
template <class Iterator>
40-
using EnableIfConvertibleToInputIterator = std::enable_if_t<std::is_convertible<
39+
namespace detail {
40+
template <typename Iterator, typename IteratorCategory>
41+
inline constexpr bool IsOfIteratorCategory = std::is_convertible_v<
4142
typename std::iterator_traits<Iterator>::iterator_category,
42-
std::input_iterator_tag>::value>;
43+
IteratorCategory>;
44+
45+
template <typename Iterator>
46+
using EnableIfConvertibleToInputIterator =
47+
std::enable_if_t<std::is_convertible_v<
48+
typename std::iterator_traits<Iterator>::iterator_category,
49+
std::input_iterator_tag>>;
50+
} // namespace detail
4351

4452
/// This is all the stuff common to all SmallVectors.
4553
///
@@ -679,13 +687,37 @@ class SmallVectorImpl : public SmallVectorTemplateBase<T> {
679687
void swap(SmallVectorImpl &RHS);
680688

681689
/// Add the specified range to the end of the SmallVector.
682-
template <typename ItTy, typename = EnableIfConvertibleToInputIterator<ItTy>>
690+
template <typename ItTy,
691+
typename = detail::EnableIfConvertibleToInputIterator<ItTy>>
683692
void append(ItTy in_start, ItTy in_end) {
684693
this->assertSafeToAddRange(in_start, in_end);
685-
size_type NumInputs = std::distance(in_start, in_end);
686-
this->reserve(this->size() + NumInputs);
687-
this->uninitialized_copy(in_start, in_end, this->end());
688-
this->set_size(this->size() + NumInputs);
694+
if constexpr (detail::IsOfIteratorCategory<
695+
ItTy, std::random_access_iterator_tag>) {
696+
// Only reserve the required extra size upfront when the size calculation
697+
// is guaranteed to be O(1).
698+
size_type NumInputs = std::distance(in_start, in_end);
699+
this->reserve(this->size() + NumInputs);
700+
this->uninitialized_copy(in_start, in_end, this->end());
701+
this->set_size(this->size() + NumInputs);
702+
} else {
703+
// Otherwise, append using `in_end` as the sentinel and reserve more space
704+
// as necessary.
705+
for (ItTy It = in_start; It != in_end;) {
706+
iterator Dest = this->end();
707+
size_type InitialSize = this->size();
708+
size_type ExtraCap = this->capacity() - InitialSize;
709+
size_type NumCopied = 0;
710+
for (; NumCopied != ExtraCap && It != in_end; ++It, ++NumCopied) {
711+
::new ((void *)(Dest + NumCopied)) T(*It);
712+
}
713+
size_type NewSize = InitialSize + NumCopied;
714+
this->set_size(NewSize);
715+
716+
if (It != in_end) {
717+
this->reserve(NewSize + 1);
718+
}
719+
}
720+
}
689721
}
690722

691723
/// Append \p NumInputs copies of \p Elt to the end.
@@ -720,7 +752,8 @@ class SmallVectorImpl : public SmallVectorTemplateBase<T> {
720752
// FIXME: Consider assigning over existing elements, rather than clearing &
721753
// re-initializing them - for all assign(...) variants.
722754

723-
template <typename ItTy, typename = EnableIfConvertibleToInputIterator<ItTy>>
755+
template <typename ItTy,
756+
typename = detail::EnableIfConvertibleToInputIterator<ItTy>>
724757
void assign(ItTy in_start, ItTy in_end) {
725758
this->assertSafeToReferenceAfterClear(in_start, in_end);
726759
clear();
@@ -871,7 +904,8 @@ class SmallVectorImpl : public SmallVectorTemplateBase<T> {
871904
return I;
872905
}
873906

874-
template <typename ItTy, typename = EnableIfConvertibleToInputIterator<ItTy>>
907+
template <typename ItTy,
908+
typename = detail::EnableIfConvertibleToInputIterator<ItTy>>
875909
iterator insert(iterator I, ItTy From, ItTy To) {
876910
// Convert iterator to elt# to avoid invalidating iterator when we reserve()
877911
size_t InsertElt = I - this->begin();
@@ -887,8 +921,8 @@ class SmallVectorImpl : public SmallVectorTemplateBase<T> {
887921
this->assertSafeToAddRange(From, To);
888922

889923
size_t NumToInsert = std::distance(From, To);
890-
891-
// Ensure there is enough space.
924+
// Ensure there is enough space so that we do not have to re-allocate mid
925+
// insertion.
892926
reserve(this->size() + NumToInsert);
893927

894928
// Uninvalidate the iterator.
@@ -1212,7 +1246,8 @@ class LLVM_GSL_OWNER SmallVector : public SmallVectorImpl<T>,
12121246
this->assign(Size, Value);
12131247
}
12141248

1215-
template <typename ItTy, typename = EnableIfConvertibleToInputIterator<ItTy>>
1249+
template <typename ItTy,
1250+
typename = detail::EnableIfConvertibleToInputIterator<ItTy>>
12161251
SmallVector(ItTy S, ItTy E) : SmallVectorImpl<T>(N) {
12171252
this->append(S, E);
12181253
}

0 commit comments

Comments
 (0)