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

Pavel Novikov via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jul 16 08:14:54 PDT 2026


https://github.com/toughengineer created https://github.com/llvm/llvm-project/pull/210078

The fix is trivial, and I wouldn't submit it otherwise, but I indeed could measure a difference in performance with slow iterators.

Here's the graph:
<img width="1210" height="681" alt="graph" src="https://github.com/user-attachments/assets/bf8b6f12-bf69-4926-8f70-e77d12da8085" />
<details>
<summary>benchmark output before</summary>

```
2026-07-16T16:53:06+03:00
Running ./string_append
Run on (20 X 3494.4 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x10)
  L1 Instruction 32 KiB (x10)
  L2 Unified 2048 KiB (x10)
  L3 Unified 24576 KiB (x1)
Load Average: 0.25, 0.23, 0.09
----------------------------------------------------------------------------------------------------
Benchmark                                          Time             CPU   Iterations UserCounters...
----------------------------------------------------------------------------------------------------
bench_append<std::vector<char>>/1               3.10 ns         3.10 ns    226315808 t/i=3.09819ns
bench_append<std::vector<char>>/2               3.10 ns         3.10 ns    223987528 t/i=1.55159ns
bench_append<std::vector<char>>/4               3.10 ns         3.10 ns    225262769 t/i=775.792ps
bench_append<std::vector<char>>/8               2.96 ns         2.96 ns    238555228 t/i=369.405ps
bench_append<std::vector<char>>/16              2.89 ns         2.89 ns    243030831 t/i=180.715ps
bench_append<std::vector<char>>/32              10.5 ns         10.5 ns     67805279 t/i=327.351ps
bench_append<std::vector<char>>/64              10.4 ns         10.4 ns     67913691 t/i=163.163ps
bench_append<std::vector<char>>/128             10.8 ns         10.8 ns     65647320 t/i=83.9901ps
bench_append<std::vector<char>>/256             10.7 ns         10.7 ns     64584881 t/i=41.8108ps
bench_append<std::vector<char>>/512             12.6 ns         12.6 ns     55021627 t/i=24.6862ps
bench_append<std::vector<char>>/1024            14.6 ns         14.6 ns     47745492 t/i=14.3039ps
bench_append<std::vector<char>>/2048            37.4 ns         37.4 ns     18517710 t/i=18.2561ps
bench_append<std::vector<char>>/4096            47.6 ns         47.6 ns     14783174 t/i=11.6187ps
bench_append<std::vector<char>>/8192            57.4 ns         57.4 ns     12362468 t/i=7.00085ps
bench_append<std::vector<char>>/16384            106 ns          106 ns      6729404 t/i=6.4528ps
bench_append<std::vector<char>>/32768            519 ns          519 ns      1368856 t/i=15.84ps
bench_append<std::vector<char>>/65536           1245 ns         1245 ns       653681 t/i=18.9985ps
bench_append<std::vector<char>>/131072          2050 ns         2050 ns       331142 t/i=15.6421ps
bench_append<std::vector<char>>/262144          4319 ns         4318 ns       161065 t/i=16.4732ps
bench_append<std::vector<char>>/524288          8290 ns         8289 ns        85569 t/i=15.8103ps
bench_append<std::vector<char>>/1048576        21300 ns        21299 ns        31262 t/i=20.3122ps
bench_append<std::list<char>>/1                 3.54 ns         3.53 ns    196033898 t/i=3.53483ns
bench_append<std::list<char>>/2                 5.27 ns         5.27 ns    134648270 t/i=2.63524ns
bench_append<std::list<char>>/4                 5.66 ns         5.66 ns    125707554 t/i=1.41568ns
bench_append<std::list<char>>/8                 9.79 ns         9.79 ns     72542694 t/i=1.22324ns
bench_append<std::list<char>>/16                14.0 ns         14.0 ns     49090700 t/i=877.378ps
bench_append<std::list<char>>/32                40.4 ns         40.4 ns     17585554 t/i=1.26376ns
bench_append<std::list<char>>/64                88.6 ns         88.6 ns      7671990 t/i=1.38454ns
bench_append<std::list<char>>/128                207 ns          207 ns      3446792 t/i=1.61596ns
bench_append<std::list<char>>/256                546 ns          546 ns      1302384 t/i=2.13168ns
bench_append<std::list<char>>/512               1062 ns         1062 ns       658620 t/i=2.07481ns
bench_append<std::list<char>>/1024              2095 ns         2095 ns       332003 t/i=2.04562ns
bench_append<std::list<char>>/2048              4254 ns         4254 ns       165516 t/i=2.07696ns
bench_append<std::list<char>>/4096              8453 ns         8453 ns        81254 t/i=2.06364ns
bench_append<std::list<char>>/8192             16727 ns        16727 ns        41643 t/i=2.04181ns
bench_append<std::list<char>>/16384            33353 ns        33314 ns        20843 t/i=2.0333ns
bench_append<std::list<char>>/32768            66781 ns        66779 ns        10635 t/i=2.03792ns
bench_append<std::list<char>>/65536           135174 ns       135170 ns         5165 t/i=2.06253ns
bench_append<std::list<char>>/131072          273614 ns       273614 ns         2543 t/i=2.08751ns
bench_append<std::list<char>>/262144          593169 ns       592477 ns         1135 t/i=2.26012ns
bench_append<std::list<char>>/524288         1328844 ns      1328848 ns          529 t/i=2.53458ns
bench_append<std::list<char>>/1048576        4477818 ns      4477612 ns          157 t/i=4.27018ns
bench_append<std::multiset<char>>/1             3.84 ns         3.84 ns    180937556 t/i=3.83834ns
bench_append<std::multiset<char>>/2             9.36 ns         9.36 ns     74438785 t/i=4.68069ns
bench_append<std::multiset<char>>/4             13.6 ns         13.6 ns     51097651 t/i=3.40815ns
bench_append<std::multiset<char>>/8             21.8 ns         21.8 ns     31933025 t/i=2.71937ns
bench_append<std::multiset<char>>/16            41.9 ns         41.9 ns     16805653 t/i=2.62013ns
bench_append<std::multiset<char>>/32            96.2 ns         96.2 ns      7354278 t/i=3.00633ns
bench_append<std::multiset<char>>/64             224 ns          224 ns      3115349 t/i=3.49983ns
bench_append<std::multiset<char>>/128            486 ns          486 ns      1440616 t/i=3.79756ns
bench_append<std::multiset<char>>/256           1013 ns         1013 ns       683716 t/i=3.95719ns
bench_append<std::multiset<char>>/512           2090 ns         2090 ns       335399 t/i=4.08226ns
bench_append<std::multiset<char>>/1024          4382 ns         4382 ns       157870 t/i=4.27957ns
bench_append<std::multiset<char>>/2048          8885 ns         8884 ns        79538 t/i=4.338ns
bench_append<std::multiset<char>>/4096         17717 ns        17716 ns        39341 t/i=4.32532ns
bench_append<std::multiset<char>>/8192         35759 ns        35756 ns        19762 t/i=4.3648ns
bench_append<std::multiset<char>>/16384        71429 ns        71427 ns         9751 t/i=4.35953ns
bench_append<std::multiset<char>>/32768       142846 ns       142839 ns         4834 t/i=4.3591ns
bench_append<std::multiset<char>>/65536       299432 ns       299430 ns         2362 t/i=4.56894ns
bench_append<std::multiset<char>>/131072      607360 ns       607356 ns         1166 t/i=4.63376ns
bench_append<std::multiset<char>>/262144     1295102 ns      1295083 ns          546 t/i=4.94035ns
bench_append<std::multiset<char>>/524288     3337828 ns      3337793 ns          209 t/i=6.36634ns
bench_append<std::multiset<char>>/1048576    8387334 ns      8387005 ns           81 t/i=7.99847ns
bench_append<Container>/1                       13.3 ns         13.3 ns     52114703 t/i=13.3287ns
bench_append<Container>/2                       23.5 ns         23.5 ns     29020259 t/i=11.771ns
bench_append<Container>/4                       48.9 ns         48.9 ns     14313064 t/i=12.2231ns
bench_append<Container>/8                       91.1 ns         91.0 ns      7690246 t/i=11.3811ns
bench_append<Container>/16                       161 ns          161 ns      4413230 t/i=10.0588ns
bench_append<Container>/32                       312 ns          312 ns      2244326 t/i=9.75056ns
bench_append<Container>/64                       583 ns          583 ns      1185685 t/i=9.11584ns
bench_append<Container>/128                     1123 ns         1122 ns       626614 t/i=8.76911ns
bench_append<Container>/256                     2197 ns         2197 ns       320017 t/i=8.58337ns
bench_append<Container>/512                     4319 ns         4319 ns       160779 t/i=8.43519ns
bench_append<Container>/1024                    8622 ns         8622 ns        79252 t/i=8.41967ns
bench_append<Container>/2048                   17239 ns        17239 ns        39849 t/i=8.41726ns
bench_append<Container>/4096                   34502 ns        34501 ns        20299 t/i=8.42297ns
bench_append<Container>/8192                   68731 ns        68728 ns        10327 t/i=8.38964ns
bench_append<Container>/16384                 137217 ns       137213 ns         5064 t/i=8.3748ns
bench_append<Container>/32768                 273730 ns       273710 ns         2518 t/i=8.35297ns
bench_append<Container>/65536                 550779 ns       550759 ns         1253 t/i=8.40392ns
bench_append<Container>/131072               1104636 ns      1104602 ns          632 t/i=8.42744ns
bench_append<Container>/262144               2192175 ns      2192081 ns          319 t/i=8.36213ns
bench_append<Container>/524288               4395209 ns      4395025 ns          160 t/i=8.38284ns
bench_append<Container>/1048576              9153992 ns      9153748 ns           77 t/i=8.72969ns
```
</details>
<details>
<summary>benchmark output after</summary>

```
2026-07-16T16:55:54+03:00
Running ./string_append
Run on (20 X 3494.4 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x10)
  L1 Instruction 32 KiB (x10)
  L2 Unified 2048 KiB (x10)
  L3 Unified 24576 KiB (x1)
Load Average: 0.26, 0.35, 0.17
----------------------------------------------------------------------------------------------------
Benchmark                                          Time             CPU   Iterations UserCounters...
----------------------------------------------------------------------------------------------------
bench_append<std::vector<char>>/1               3.09 ns         3.09 ns    227887283 t/i=3.0944ns
bench_append<std::vector<char>>/2               3.10 ns         3.09 ns    226039491 t/i=1.54692ns
bench_append<std::vector<char>>/4               3.10 ns         3.10 ns    225739021 t/i=774.206ps
bench_append<std::vector<char>>/8               3.10 ns         3.09 ns    224468619 t/i=386.875ps
bench_append<std::vector<char>>/16              2.91 ns         2.91 ns    240031986 t/i=182.18ps
bench_append<std::vector<char>>/32              10.9 ns         10.9 ns     65077293 t/i=339.745ps
bench_append<std::vector<char>>/64              10.9 ns         10.9 ns     63585149 t/i=170.175ps
bench_append<std::vector<char>>/128             11.1 ns         11.1 ns     63889890 t/i=86.7411ps
bench_append<std::vector<char>>/256             11.0 ns         11.0 ns     62736325 t/i=43.117ps
bench_append<std::vector<char>>/512             13.1 ns         13.1 ns     53444253 t/i=25.5178ps
bench_append<std::vector<char>>/1024            14.9 ns         14.9 ns     46805617 t/i=14.5771ps
bench_append<std::vector<char>>/2048            37.6 ns         37.6 ns     18720091 t/i=18.3699ps
bench_append<std::vector<char>>/4096            47.6 ns         47.6 ns     14738139 t/i=11.6281ps
bench_append<std::vector<char>>/8192            57.3 ns         57.3 ns     12354002 t/i=6.99105ps
bench_append<std::vector<char>>/16384            105 ns          105 ns      6534679 t/i=6.4209ps
bench_append<std::vector<char>>/32768            520 ns          520 ns      1368420 t/i=15.8662ps
bench_append<std::vector<char>>/65536           1043 ns         1043 ns       665359 t/i=15.9218ps
bench_append<std::vector<char>>/131072          2074 ns         2073 ns       339638 t/i=15.8193ps
bench_append<std::vector<char>>/262144          4144 ns         4144 ns       160746 t/i=15.8074ps
bench_append<std::vector<char>>/524288          8200 ns         8200 ns        80639 t/i=15.6401ps
bench_append<std::vector<char>>/1048576        21893 ns        21892 ns        30043 t/i=20.8781ps
bench_append<std::list<char>>/1                 3.55 ns         3.55 ns    199315153 t/i=3.5478ns
bench_append<std::list<char>>/2                 5.29 ns         5.29 ns    134176216 t/i=2.64552ns
bench_append<std::list<char>>/4                 5.49 ns         5.49 ns    123464411 t/i=1.37183ns
bench_append<std::list<char>>/8                 8.85 ns         8.85 ns     80565431 t/i=1.10657ns
bench_append<std::list<char>>/16                12.5 ns         12.5 ns     55630347 t/i=783.169ps
bench_append<std::list<char>>/32                39.6 ns         39.6 ns     17583818 t/i=1.23641ns
bench_append<std::list<char>>/64                89.0 ns         89.0 ns      8200063 t/i=1.39032ns
bench_append<std::list<char>>/128                204 ns          204 ns      3371002 t/i=1.59479ns
bench_append<std::list<char>>/256                546 ns          546 ns      1256033 t/i=2.13231ns
bench_append<std::list<char>>/512               1065 ns         1065 ns       648641 t/i=2.08083ns
bench_append<std::list<char>>/1024              2102 ns         2102 ns       334077 t/i=2.05267ns
bench_append<std::list<char>>/2048              4249 ns         4249 ns       164325 t/i=2.07457ns
bench_append<std::list<char>>/4096              8433 ns         8433 ns        84268 t/i=2.05886ns
bench_append<std::list<char>>/8192             16685 ns        16685 ns        41640 t/i=2.03673ns
bench_append<std::list<char>>/16384            33543 ns        33543 ns        21102 t/i=2.04728ns
bench_append<std::list<char>>/32768            67025 ns        67022 ns        10656 t/i=2.04534ns
bench_append<std::list<char>>/65536           135122 ns       135120 ns         5220 t/i=2.06176ns
bench_append<std::list<char>>/131072          274342 ns       274328 ns         2542 t/i=2.09296ns
bench_append<std::list<char>>/262144          591436 ns       591421 ns         1181 t/i=2.25609ns
bench_append<std::list<char>>/524288         1389493 ns      1388228 ns          523 t/i=2.64784ns
bench_append<std::list<char>>/1048576        4536406 ns      4536297 ns          153 t/i=4.32615ns
bench_append<std::multiset<char>>/1             3.87 ns         3.87 ns    181230524 t/i=3.87176ns
bench_append<std::multiset<char>>/2             9.51 ns         9.51 ns     71901138 t/i=4.75509ns
bench_append<std::multiset<char>>/4             13.2 ns         13.2 ns     52352810 t/i=3.29722ns
bench_append<std::multiset<char>>/8             21.7 ns         21.7 ns     32439441 t/i=2.71142ns
bench_append<std::multiset<char>>/16            40.0 ns         40.0 ns     17563514 t/i=2.49991ns
bench_append<std::multiset<char>>/32            96.8 ns         96.8 ns      7208224 t/i=3.0244ns
bench_append<std::multiset<char>>/64             225 ns          225 ns      3091425 t/i=3.5148ns
bench_append<std::multiset<char>>/128            488 ns          488 ns      1443983 t/i=3.81319ns
bench_append<std::multiset<char>>/256           1014 ns         1014 ns       700744 t/i=3.96008ns
bench_append<std::multiset<char>>/512           2087 ns         2087 ns       333876 t/i=4.07604ns
bench_append<std::multiset<char>>/1024          4405 ns         4404 ns       159590 t/i=4.30126ns
bench_append<std::multiset<char>>/2048          8904 ns         8904 ns        79107 t/i=4.34771ns
bench_append<std::multiset<char>>/4096         17856 ns        17855 ns        39341 t/i=4.35914ns
bench_append<std::multiset<char>>/8192         35414 ns        35413 ns        19531 t/i=4.32291ns
bench_append<std::multiset<char>>/16384        71063 ns        70984 ns         9626 t/i=4.33251ns
bench_append<std::multiset<char>>/32768       145779 ns       145774 ns         4834 t/i=4.44867ns
bench_append<std::multiset<char>>/65536       304988 ns       304974 ns         2357 t/i=4.65353ns
bench_append<std::multiset<char>>/131072      604942 ns       602719 ns         1128 t/i=4.59838ns
bench_append<std::multiset<char>>/262144     1267769 ns      1267755 ns          560 t/i=4.8361ns
bench_append<std::multiset<char>>/524288     3324835 ns      3324630 ns          213 t/i=6.34123ns
bench_append<std::multiset<char>>/1048576    8387543 ns      8387384 ns           82 t/i=7.99883ns
bench_append<Container>/1                       10.4 ns         10.4 ns     66804092 t/i=10.3965ns
bench_append<Container>/2                       16.8 ns         16.8 ns     41695556 t/i=8.39998ns
bench_append<Container>/4                       34.5 ns         34.5 ns     20452545 t/i=8.62682ns
bench_append<Container>/8                       62.9 ns         62.9 ns     11109242 t/i=7.86804ns
bench_append<Container>/16                       111 ns          111 ns      6422949 t/i=6.91392ns
bench_append<Container>/32                       216 ns          216 ns      3276646 t/i=6.76101ns
bench_append<Container>/64                       400 ns          400 ns      1745608 t/i=6.2475ns
bench_append<Container>/128                      765 ns          765 ns       928239 t/i=5.97368ns
bench_append<Container>/256                     1489 ns         1489 ns       468787 t/i=5.8147ns
bench_append<Container>/512                     2942 ns         2942 ns       238431 t/i=5.74671ns
bench_append<Container>/1024                    5837 ns         5837 ns       121633 t/i=5.69982ns
bench_append<Container>/2048                   11727 ns        11726 ns        60224 t/i=5.72563ns
bench_append<Container>/4096                   23304 ns        23303 ns        30075 t/i=5.68928ns
bench_append<Container>/8192                   46442 ns        46440 ns        15129 t/i=5.66892ns
bench_append<Container>/16384                  92749 ns        92748 ns         7607 t/i=5.66086ns
bench_append<Container>/32768                 185188 ns       185181 ns         3759 t/i=5.65128ns
bench_append<Container>/65536                 371024 ns       371014 ns         1888 t/i=5.66122ns
bench_append<Container>/131072                739596 ns       739562 ns          958 t/i=5.64241ns
bench_append<Container>/262144               1487139 ns      1487121 ns          477 t/i=5.67292ns
bench_append<Container>/524288               2963851 ns      2963776 ns          237 t/i=5.65295ns
bench_append<Container>/1048576              6313121 ns      6313038 ns          112 t/i=6.02058ns
```
</details>

The "elapsed time" on the graph (`t/i` in benchmark output, time per item) is elapsed time for the append operation divided by the number of elements/characters appended.

`Container` is basically a `std::vector<char>` with wrapped iterators with increment operation slowed down by a call to `rand()`.

Otherwise I could not measure a significant difference for `std::list<char>` or `std::multiset<char>`, those can be seen as reference and control values.

To benchmark it I used Clang 22.1.2 on Ubuntu 26.04 inside WSL2.

>From 8b114a55b16eb39a3005ad81c2cb0ae5d8d42347 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] [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);



More information about the libcxx-commits mailing list