[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