[libcxx-commits] [libcxx] [libc++] fix minor performance issue in `basic_string<C>::append()` (PR #210078)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jul 21 05:06:19 PDT 2026


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/210078

>From 34419f2461f3cc4ac0c285cfbb76e64f016db652 Mon Sep 17 00:00:00 2001
From: Pavel Novikov <dev-ape at yandex.ru>
Date: Thu, 16 Jul 2026 17:43:25 +0300
Subject: [PATCH 1/2] [libc++] fix minor performance issue

---
 libcxx/include/string | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index a297a05813cee..957eb15e4fe1f 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -1359,13 +1359,13 @@ public:
   template <class _ForwardIterator, __enable_if_t<__has_forward_iterator_category<_ForwardIterator>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
   append(_ForwardIterator __first, _ForwardIterator __last) {
-    size_type __sz  = size();
-    size_type __cap = capacity();
-    size_type __n   = static_cast<size_type>(std::distance(__first, __last));
-    if (__n == 0)
+    if (__first == __last)
       return *this;
 
     if (__string_is_trivial_iterator_v<_ForwardIterator> && !__addr_in_range(*__first)) {
+      size_type __sz  = size();
+      size_type __cap = capacity();
+      size_type __n   = static_cast<size_type>(std::distance(__first, __last));
       if (__cap - __sz < __n)
         __grow_by_without_replace(__cap, __sz + __n - __cap, __sz, __sz, 0);
       __annotate_increase(__n);

>From bd806f470a02647245b01bfa9209fc4937699bc0 Mon Sep 17 00:00:00 2001
From: Pavel Novikov <dev-ape at yandex.ru>
Date: Tue, 21 Jul 2026 14:42:18 +0300
Subject: [PATCH 2/2] [libc++] added benchmarks for `string::append()`

---
 .../benchmarks/containers/string.bench.cpp    | 40 +++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/libcxx/test/benchmarks/containers/string.bench.cpp b/libcxx/test/benchmarks/containers/string.bench.cpp
index 776ee5f58b7e2..5ad80d1d3ec5a 100644
--- a/libcxx/test/benchmarks/containers/string.bench.cpp
+++ b/libcxx/test/benchmarks/containers/string.bench.cpp
@@ -17,6 +17,7 @@
 #include "benchmark/benchmark.h"
 #include "make_string.h"
 #include "test_macros.h"
+#include "test_iterators.h"
 
 constexpr size_t small_size = 5;
 constexpr size_t large_size = 30;
@@ -502,6 +503,45 @@ int main(int argc, char** argv) {
           [](auto bm) { bm->Arg(large_size); }); // for naming
   }
 
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> src(size, 'a');
+          auto getIterator = [&src](size_t i) {
+            // INT_MAX because we want overhead of ThrowingIterator without actually throwing
+            return ThrowingIterator<CharT>(src.data() + i, src.data() + src.size(), INT_MAX);
+          };
+          std::basic_string<CharT> string = src;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(string);
+            string.clear();
+            if constexpr (opaque)
+              benchmark::DoNotOptimize(src);
+            benchmark::DoNotOptimize(string.append(getIterator(0), getIterator(size)));
+          }
+        };
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(large_size); }); // for naming
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(large_size); }); // for naming
+  }
+
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();
   benchmark::Shutdown();



More information about the libcxx-commits mailing list