[llvm] [WIP][ADT] Avoid slow size queries on append (PR #136365)
Jakub Kuderski via llvm-commits
llvm-commits at lists.llvm.org
Fri Apr 18 13:55:35 PDT 2025
https://github.com/kuhar updated https://github.com/llvm/llvm-project/pull/136365
>From 078be5edcf584d380718f6e5b7d10325065d219a Mon Sep 17 00:00:00 2001
From: Jakub Kuderski <jakub at nod-labs.com>
Date: Fri, 18 Apr 2025 16:24:20 -0400
Subject: [PATCH 1/2] [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:
https://github.com/llvm/llvm-project/pull/136066#issuecomment-2814895109.
---
llvm/include/llvm/ADT/SmallVector.h | 61 +++++++++++++++++++++++------
1 file changed, 48 insertions(+), 13 deletions(-)
diff --git a/llvm/include/llvm/ADT/SmallVector.h b/llvm/include/llvm/ADT/SmallVector.h
index bd3e887e36bce..104a3aaf79418 100644
--- a/llvm/include/llvm/ADT/SmallVector.h
+++ b/llvm/include/llvm/ADT/SmallVector.h
@@ -36,10 +36,18 @@ template <typename T> class ArrayRef;
template <typename IteratorT> class iterator_range;
-template <class Iterator>
-using EnableIfConvertibleToInputIterator = std::enable_if_t<std::is_convertible<
+namespace detail {
+template <typename Iterator, typename IteratorCategory>
+inline constexpr bool IsOfIteratorCategory = std::is_convertible_v<
typename std::iterator_traits<Iterator>::iterator_category,
- std::input_iterator_tag>::value>;
+ IteratorCategory>;
+
+template <typename Iterator>
+using EnableIfConvertibleToInputIterator =
+ std::enable_if_t<std::is_convertible_v<
+ typename std::iterator_traits<Iterator>::iterator_category,
+ std::input_iterator_tag>>;
+} // namespace detail
/// This is all the stuff common to all SmallVectors.
///
@@ -679,13 +687,37 @@ class SmallVectorImpl : public SmallVectorTemplateBase<T> {
void swap(SmallVectorImpl &RHS);
/// Add the specified range to the end of the SmallVector.
- template <typename ItTy, typename = EnableIfConvertibleToInputIterator<ItTy>>
+ template <typename ItTy,
+ typename = detail::EnableIfConvertibleToInputIterator<ItTy>>
void append(ItTy in_start, ItTy in_end) {
this->assertSafeToAddRange(in_start, in_end);
- size_type NumInputs = std::distance(in_start, in_end);
- this->reserve(this->size() + NumInputs);
- this->uninitialized_copy(in_start, in_end, this->end());
- this->set_size(this->size() + NumInputs);
+ if constexpr (detail::IsOfIteratorCategory<
+ ItTy, std::random_access_iterator_tag>) {
+ // Only reserve the required extra size upfront when the size calculation
+ // is guaranteed to be O(1).
+ size_type NumInputs = std::distance(in_start, in_end);
+ this->reserve(this->size() + NumInputs);
+ this->uninitialized_copy(in_start, in_end, this->end());
+ this->set_size(this->size() + NumInputs);
+ } else {
+ // Otherwise, append using `in_end` as the sentinel and reserve more space
+ // as necessary.
+ for (ItTy It = in_start; It != in_end;) {
+ iterator Dest = this->end();
+ size_type InitialSize = this->size();
+ size_type ExtraCap = this->capacity() - InitialSize;
+ size_type NumCopied = 0;
+ for (; NumCopied != ExtraCap && It != in_end; ++It, ++NumCopied) {
+ ::new ((void *)(Dest + NumCopied)) T(*It);
+ }
+ size_type NewSize = InitialSize + NumCopied;
+ this->set_size(NewSize);
+
+ if (It != in_end) {
+ this->reserve(NewSize + 1);
+ }
+ }
+ }
}
/// Append \p NumInputs copies of \p Elt to the end.
@@ -720,7 +752,8 @@ class SmallVectorImpl : public SmallVectorTemplateBase<T> {
// FIXME: Consider assigning over existing elements, rather than clearing &
// re-initializing them - for all assign(...) variants.
- template <typename ItTy, typename = EnableIfConvertibleToInputIterator<ItTy>>
+ template <typename ItTy,
+ typename = detail::EnableIfConvertibleToInputIterator<ItTy>>
void assign(ItTy in_start, ItTy in_end) {
this->assertSafeToReferenceAfterClear(in_start, in_end);
clear();
@@ -871,7 +904,8 @@ class SmallVectorImpl : public SmallVectorTemplateBase<T> {
return I;
}
- template <typename ItTy, typename = EnableIfConvertibleToInputIterator<ItTy>>
+ template <typename ItTy,
+ typename = detail::EnableIfConvertibleToInputIterator<ItTy>>
iterator insert(iterator I, ItTy From, ItTy To) {
// Convert iterator to elt# to avoid invalidating iterator when we reserve()
size_t InsertElt = I - this->begin();
@@ -887,8 +921,8 @@ class SmallVectorImpl : public SmallVectorTemplateBase<T> {
this->assertSafeToAddRange(From, To);
size_t NumToInsert = std::distance(From, To);
-
- // Ensure there is enough space.
+ // Ensure there is enough space so that we do not have to re-allocate mid
+ // insertion.
reserve(this->size() + NumToInsert);
// Uninvalidate the iterator.
@@ -1212,7 +1246,8 @@ class LLVM_GSL_OWNER SmallVector : public SmallVectorImpl<T>,
this->assign(Size, Value);
}
- template <typename ItTy, typename = EnableIfConvertibleToInputIterator<ItTy>>
+ template <typename ItTy,
+ typename = detail::EnableIfConvertibleToInputIterator<ItTy>>
SmallVector(ItTy S, ItTy E) : SmallVectorImpl<T>(N) {
this->append(S, E);
}
>From 3d891d2893e978b9baf08eb2dcd2305ef5a31dc8 Mon Sep 17 00:00:00 2001
From: Jakub Kuderski <jakub at nod-labs.com>
Date: Fri, 18 Apr 2025 16:55:26 -0400
Subject: [PATCH 2/2] Address comments
---
llvm/include/llvm/ADT/SmallVector.h | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/llvm/include/llvm/ADT/SmallVector.h b/llvm/include/llvm/ADT/SmallVector.h
index 104a3aaf79418..0f89ecd323e5a 100644
--- a/llvm/include/llvm/ADT/SmallVector.h
+++ b/llvm/include/llvm/ADT/SmallVector.h
@@ -707,9 +707,9 @@ class SmallVectorImpl : public SmallVectorTemplateBase<T> {
size_type InitialSize = this->size();
size_type ExtraCap = this->capacity() - InitialSize;
size_type NumCopied = 0;
- for (; NumCopied != ExtraCap && It != in_end; ++It, ++NumCopied) {
+ for (; NumCopied != ExtraCap && It != in_end; ++It, ++NumCopied)
::new ((void *)(Dest + NumCopied)) T(*It);
- }
+
size_type NewSize = InitialSize + NumCopied;
this->set_size(NewSize);
@@ -910,7 +910,8 @@ class SmallVectorImpl : public SmallVectorTemplateBase<T> {
// Convert iterator to elt# to avoid invalidating iterator when we reserve()
size_t InsertElt = I - this->begin();
- if (I == this->end()) { // Important special case for empty vector.
+ // Important special case for appending to a vector, including empty vector.
+ if (I == this->end()) {
append(From, To);
return this->begin()+InsertElt;
}
More information about the llvm-commits
mailing list