Skip to content

Commit f4596cb

Browse files
committed
[libc++] Optimize basic_string::append(Iter, Iter)
1 parent d6be9fc commit f4596cb

File tree

2 files changed

+60
-13
lines changed

2 files changed

+60
-13
lines changed

libcxx/include/string

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,24 +1440,48 @@ public:
14401440
template <class _ForwardIterator, __enable_if_t<__has_forward_iterator_category<_ForwardIterator>::value, int> = 0>
14411441
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
14421442
append(_ForwardIterator __first, _ForwardIterator __last) {
1443-
size_type __sz = size();
1444-
size_type __cap = capacity();
1445-
size_type __n = static_cast<size_type>(std::distance(__first, __last));
1443+
const size_type __size = size();
1444+
const size_type __cap = capacity();
1445+
const size_type __n = static_cast<size_type>(std::distance(__first, __last));
14461446
if (__n == 0)
14471447
return *this;
14481448

1449-
if (__string_is_trivial_iterator_v<_ForwardIterator> && !__addr_in_range(*__first)) {
1450-
if (__cap - __sz < __n)
1451-
__grow_by_without_replace(__cap, __sz + __n - __cap, __sz, __sz, 0);
1452-
__annotate_increase(__n);
1453-
auto __end = __copy_non_overlapping_range(__first, __last, std::__to_address(__get_pointer() + __sz));
1454-
traits_type::assign(*__end, value_type());
1455-
__set_size(__sz + __n);
1456-
return *this;
1449+
if (__n > __cap - __size) {
1450+
__long __buffer = __allocate_long_buffer(__alloc_, __get_amortized_growth_capacity(__size + __n));
1451+
__buffer.__size_ = __size + __n;
1452+
auto __guard = std::__make_exception_guard([&] {
1453+
__alloc_traits::deallocate(__alloc_, __buffer.__data_, __buffer.__cap_ * __endian_factor);
1454+
});
1455+
auto __end = __copy_non_overlapping_range(__first, __last, std::__to_address(__buffer.__data_) + __size);
1456+
traits_type::assign(*__end, _CharT());
1457+
traits_type::copy(std::__to_address(__buffer.__data_), data(), __size);
1458+
__guard.__complete();
1459+
__reset_internal_buffer(__buffer);
14571460
} else {
1458-
const basic_string __temp(__first, __last, __alloc_);
1459-
return append(__temp.data(), __temp.size());
1461+
_CharT* const __ptr = std::__to_address(__get_pointer());
1462+
# ifndef _LIBCPP_CXX03_LANG
1463+
if constexpr (__libcpp_is_contiguous_iterator<_ForwardIterator>::value &&
1464+
is_same<value_type, __remove_cvref_t<decltype(*__first)>>::value) {
1465+
traits_type::move(__ptr + __size, std::__to_address(__first), __last - __first);
1466+
} else if constexpr (__has_bidirectional_iterator_category<_ForwardIterator>::value) {
1467+
auto __dest = __ptr + __size + __n;
1468+
while (__first != __last) {
1469+
--__last;
1470+
--__dest;
1471+
traits_type::assign(*__dest, *__last);
1472+
}
1473+
} else
1474+
# endif
1475+
{
1476+
_CharT __first_val = *__first;
1477+
++__first;
1478+
__copy_non_overlapping_range(__first, __last, __ptr + __size + 1);
1479+
traits_type::assign(__ptr[__size], __first_val);
1480+
}
1481+
traits_type::assign(__ptr[__size + __n], _CharT());
1482+
__set_size(__size + __n);
14601483
}
1484+
return *this;
14611485
}
14621486

14631487
# if _LIBCPP_STD_VER >= 23

libcxx/test/benchmarks/containers/string.bench.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
#include <algorithm>
1212
#include <cstdint>
1313
#include <cstdlib>
14+
#include <deque>
15+
#include <iterator>
1416
#include <new>
17+
#include <list>
1518
#include <vector>
1619

1720
#include "../CartesianBenchmarks.h"
@@ -122,6 +125,26 @@ static void BM_StringResizeAndOverwrite(benchmark::State& state) {
122125
}
123126
BENCHMARK(BM_StringResizeAndOverwrite);
124127

128+
template <class Container>
129+
static void BM_StringAppend(benchmark::State& state) {
130+
std::string orig_str = "This is some data so the string isn't empty";
131+
std::string str = orig_str;
132+
133+
const char to_append_str[] = "This is a long string to make sure append does some work";
134+
Container to_append;
135+
std::copy(std::begin(to_append_str), std::end(to_append_str), std::back_inserter(to_append));
136+
137+
for (auto _ : state) {
138+
benchmark::DoNotOptimize(str);
139+
str.append(to_append.begin(), to_append.end());
140+
benchmark::DoNotOptimize(str);
141+
str.resize(orig_str.size());
142+
}
143+
}
144+
BENCHMARK(BM_StringAppend<std::vector<char>>);
145+
BENCHMARK(BM_StringAppend<std::deque<char>>);
146+
BENCHMARK(BM_StringAppend<std::list<char>>);
147+
125148
enum class Length { Empty, Small, Large, Huge };
126149
struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
127150
static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};

0 commit comments

Comments
 (0)