<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/155410>155410</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
[libc++][perf] Is there a reason appending to `std::string` is much slower than to `std::vector<char>`?
</td>
</tr>
<tr>
<th>Labels</th>
<td>
libc++
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
toughengineer
</td>
</tr>
</table>
<pre>
# tl;dr:
Considering `std::string` is (supposed to be) tailored to handle characters, and implements small buffer optimization which should make appending to it a bit cheaper even for considerable eventual size of the resulting string, appending to `std::string` is significantly slower than inserting at the end into `std::vector<char>`.
Is there a particular reason for that?
# Benchmark results
I ran benchmarks inside Ubuntu 24.04.1 LTS inside WSL on i5 13600k CPU using latest Clang 20.1.
I used option `--benchmark_min_warmup_time=0.1` to make results more stable so the benchmark was launched like this:
```
./bench --benchmark_min_warmup_time=0.1
```
The benchmarks looks like this:
```c++
void vector_push_back(benchmark::State& state) {
for (auto $ : state) {
std::vector<char> v;
//v.reserve(Count);
for (size_t i = 0; i != Count; ++i) {
v.push_back('0');
benchmark::DoNotOptimize(v);
}
}
state.counters["rate"] = benchmark::Counter{ static_cast<double>(Count),
benchmark::Counter::kIsIterationInvariantRate };
}
```
I.e. a chunk of characters is appended to a container `Count = 1000` times, in this example the chunk is just one character.
I compared **rate** as reported by the benchmarks which is amount of bytes appended divided by CPU time.
<details><summary>Here is the complete code. (Click/tap to expand.)</summary>
```c++
#include <vector>
#include <string>
#include <string_view>
#include <cmath>
#include <benchmark/benchmark.h>
using namespace std::string_view_literals;
const auto data =
"012345678901234567890123456789012345678901234567890123456789"
"012345678901234567890123456789012345678901234567890123456789"
"012345678901234567890123456789012345678901234567890123456789"
"012345678901234567890123456789012345678901234567890123456789"
"012345678901234567890123456789012345678901234567890123456789"
"012345678901234567890123456789012345678901234567890123456789"
"012345678901234567890123456789012345678901234567890123456789"
"012345678901234567890123456789012345678901234567890123456789"
"012345678901234567890123456789012345678901234567890123456789"
"012345678901234567890123456789012345678901234567890123456789"
"012345678901234567890123456789012345678901234567890123456789"
"012345678901234567890123456789012345678901234567890123456789"
"012345678901234567890123456789012345678901234567890123456789"
"012345678901234567890123456789012345678901234567890123456789"
"012345678901234567890123456789012345678901234567890123456789"
"012345678901234567890123456789012345678901234567890123456789"
"012345678901234567890123456789012345678901234567890123456789"
"0123"sv;
const size_t Count = 1000;
void dummy(benchmark::State& state) {
for (auto $ : state) {
size_t s = 0;
for (size_t i = 0; i != Count; ++i) {
++s;
benchmark::DoNotOptimize(s);
}
}
state.counters["rate"] = benchmark::Counter{ static_cast<double>(Count),
benchmark::Counter::kIsIterationInvariantRate };
}
void vector_push_back(benchmark::State& state) {
for (auto $ : state) {
std::vector<char> v;
//v.reserve(Count);
for (size_t i = 0; i != Count; ++i) {
v.push_back('0');
benchmark::DoNotOptimize(v);
}
}
state.counters["rate"] = benchmark::Counter{ static_cast<double>(Count),
benchmark::Counter::kIsIterationInvariantRate };
}
void vector_insert(benchmark::State & state) {
const auto d = data.substr(0, state.range());
for (auto $ : state) {
std::vector<char> v;
//v.reserve(d.size() * Count);
for (size_t i = 0; i != Count; ++i) {
v.insert(v.end(), d.begin(), d.end());
benchmark::DoNotOptimize(v);
}
}
state.counters["rate"] = benchmark::Counter{ static_cast<double>(state.range() * Count),
benchmark::Counter::kIsIterationInvariantRate };
}
void string_push_back(benchmark::State& state) {
for (auto $ : state) {
std::string s;
//s.reserve(Count);
for (size_t i = 0; i != Count; ++i) {
s.push_back('0');
benchmark::DoNotOptimize(s);
}
}
state.counters["rate"] = benchmark::Counter{ static_cast<double>(Count),
benchmark::Counter::kIsIterationInvariantRate };
}
void string_append(benchmark::State & state) {
const auto d = data.substr(0, state.range());
for (auto $ : state) {
std::string s;
//s.reserve(d.size() * Count);
for (size_t i = 0; i != Count; ++i) {
s.append(d);
benchmark::DoNotOptimize(s);
}
}
state.counters["rate"] = benchmark::Counter{ static_cast<double>(state.range() * Count),
benchmark::Counter::kIsIterationInvariantRate };
}
constexpr size_t RangeMultiplierSquared = 2;
constexpr size_t DataSize = 512;
template<typename B>
void Args(B b);
BENCHMARK(dummy);
BENCHMARK(vector_push_back);
BENCHMARK(vector_insert)->Apply(Args)->Arg(DataSize * 2);
BENCHMARK(string_push_back);
BENCHMARK(string_append)->Apply(Args)->Arg(DataSize * 2);
int64_t roundedBefore(int64_t i) {
return static_cast<int64_t>(round(i * pow(RangeMultiplierSquared, -0.5)));
}
template<typename B>
void Args(B b) {
b->Args({ 1 });
int i = RangeMultiplierSquared;
if (const auto k = roundedBefore(i); k != 1)
b->Args({ k });
b->Args({ i });
while ((i *= RangeMultiplierSquared) < DataSize) {
b->Args({ roundedBefore(i) });
b->Args({ i });
}
if (const auto k = roundedBefore(i); k < DataSize)
b->Args({ k });
b->Args({ DataSize });
};
```
Here `dummy` is just a control to gauge problems.
`Args` function template sets up sizes so that there is an additional data point roughly between powers of two on a logarithmic scale.
For some reason the last run gives incorrect measurements sometimes, I added sacrificial data points at the end to mitigate that (`DataSize * 2`).
</details>
## Results

<details><summary>Full benchmark output. (Click/tap to expand.)</summary>
```
2025-08-26T14:12:56+03:00
Running ./bench
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.00, 0.03, 0.08
-----------------------------------------------------------------------------
Benchmark Time CPU Iterations UserCounters...
-----------------------------------------------------------------------------
dummy 227 ns 209 ns 3356710 rate=4.77757G/s
vector_push_back 530 ns 489 ns 1420428 rate=2.0451G/s
vector_insert/1 1246 ns 1151 ns 608950 rate=869.157M/s
vector_insert/2 1485 ns 1371 ns 510698 rate=1.45902G/s
vector_insert/3 1637 ns 1511 ns 464718 rate=1.98556G/s
vector_insert/4 1907 ns 1760 ns 399303 rate=2.2721G/s
vector_insert/6 2219 ns 2049 ns 340955 rate=2.9287G/s
vector_insert/8 1903 ns 1757 ns 397994 rate=4.55447G/s
vector_insert/11 2451 ns 2263 ns 309396 rate=4.86143G/s
vector_insert/16 2710 ns 2501 ns 279629 rate=6.39634G/s
vector_insert/23 4064 ns 3751 ns 186611 rate=6.13099G/s
vector_insert/32 1851 ns 1708 ns 409604 rate=18.7305G/s
vector_insert/45 4019 ns 3710 ns 188916 rate=12.1305G/s
vector_insert/64 3436 ns 3172 ns 220936 rate=20.1796G/s
vector_insert/91 23445 ns 21476 ns 33860 rate=4.2373G/s
vector_insert/128 36662 ns 33703 ns 20895 rate=3.7979G/s
vector_insert/181 61004 ns 55933 ns 12640 rate=3.23601G/s
vector_insert/256 90194 ns 83002 ns 8457 rate=3.08425G/s
vector_insert/362 137841 ns 127006 ns 5485 rate=2.85025G/s
vector_insert/512 203347 ns 187528 ns 3777 rate=2.73027G/s
vector_insert/1024 443245 ns 409018 ns 1713 rate=2.50356G/s
string_push_back 1645 ns 1519 ns 459028 rate=658.405M/s
string_append/1 4398 ns 4060 ns 171797 rate=246.306M/s
string_append/2 4295 ns 3965 ns 175805 rate=504.405M/s
string_append/3 4357 ns 4022 ns 175788 rate=745.915M/s
string_append/4 4348 ns 4013 ns 174600 rate=996.641M/s
string_append/6 4286 ns 3956 ns 176163 rate=1.51661G/s
string_append/8 4386 ns 4048 ns 173727 rate=1.97618G/s
string_append/11 4377 ns 4040 ns 172980 rate=2.72269G/s
string_append/16 4393 ns 4056 ns 171781 rate=3.94523G/s
string_append/23 4619 ns 4264 ns 165310 rate=5.39399G/s
string_append/32 4567 ns 4215 ns 165359 rate=7.59122G/s
string_append/45 4509 ns 4162 ns 167575 rate=10.8112G/s
string_append/64 5094 ns 4702 ns 150211 rate=13.6121G/s
string_append/91 6280 ns 5797 ns 120507 rate=15.6978G/s
string_append/128 7148 ns 6598 ns 105000 rate=19.3984G/s
string_append/181 10105 ns 9328 ns 75234 rate=19.4047G/s
string_append/256 11624 ns 10730 ns 65008 rate=23.8591G/s
string_append/362 15333 ns 14153 ns 49536 rate=25.5773G/s
string_append/512 19747 ns 18228 ns 38433 rate=28.0885G/s
string_append/1024 449037 ns 414404 ns 1705 rate=2.47102G/s
```
</details>
On the right some crazy things happen to `std::vector<char>` which I can not fully explain (probably data set exceeding L3 size plays a big role), instead let's focus on "the good part" on the left.

Here `std::string` is **68% slower** than `std::vector<char>` regarding `push_back()`;
and around **40% to 70% slower** regarding appending characters in chunks depending on the chunk size (using vector `insert()` and string `append()`).
One can think that maybe different allocation patterns of `std::string` and `std::vector` play a role, and indeed they do play a role, so instead of investigating it I eliminated it altogether by reserving necessary storage (uncommented `reserve()` calls).
## Results with `reserve()`

<details><summary>Full benchmark output. (Click/tap to expand.)</summary>
```
2025-08-26T14:19:29+03:00
Running ./bench
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.40, 0.20, 0.13
-----------------------------------------------------------------------------
Benchmark Time CPU Iterations UserCounters...
-----------------------------------------------------------------------------
dummy 226 ns 208 ns 3361395 rate=4.79691G/s
vector_push_back 439 ns 405 ns 1726001 rate=2.46609G/s
vector_insert/1 997 ns 920 ns 764443 rate=1.0865G/s
vector_insert/2 1230 ns 1135 ns 610784 rate=1.76206G/s
vector_insert/3 1440 ns 1330 ns 524039 rate=2.25626G/s
vector_insert/4 1572 ns 1451 ns 487840 rate=2.75743G/s
vector_insert/6 2006 ns 1852 ns 374242 rate=3.24026G/s
vector_insert/8 1560 ns 1440 ns 481767 rate=5.55526G/s
vector_insert/11 2199 ns 2030 ns 345151 rate=5.41978G/s
vector_insert/16 2424 ns 2238 ns 313185 rate=7.15004G/s
vector_insert/23 3759 ns 3470 ns 200937 rate=6.62851G/s
vector_insert/32 1114 ns 1028 ns 678571 rate=31.1138G/s
vector_insert/45 3111 ns 2872 ns 244499 rate=15.6689G/s
vector_insert/64 2012 ns 1857 ns 379973 rate=34.4563G/s
vector_insert/91 5697 ns 5259 ns 133134 rate=17.3044G/s
vector_insert/128 3046 ns 2812 ns 249728 rate=45.5218G/s
vector_insert/181 6762 ns 6241 ns 111984 rate=28.9997G/s
vector_insert/256 5731 ns 5290 ns 117313 rate=48.3906G/s
vector_insert/362 9787 ns 9034 ns 83134 rate=40.0707G/s
vector_insert/512 7479 ns 6904 ns 101189 rate=74.1609G/s
vector_insert/1024 13777 ns 12717 ns 55029 rate=80.5231G/s
string_push_back 1453 ns 1342 ns 524367 rate=745.401M/s
string_append/1 4704 ns 4343 ns 160682 rate=230.276M/s
string_append/2 4347 ns 4013 ns 173773 rate=498.401M/s
string_append/3 4346 ns 4011 ns 174178 rate=747.867M/s
string_append/4 4421 ns 4081 ns 172674 rate=980.083M/s
string_append/6 4434 ns 4093 ns 166934 rate=1.46594G/s
string_append/8 4400 ns 4062 ns 173671 rate=1.96953G/s
string_append/11 4420 ns 4080 ns 174113 rate=2.6962G/s
string_append/16 4499 ns 4153 ns 169456 rate=3.85279G/s
string_append/23 4542 ns 4193 ns 167624 rate=5.48576G/s
string_append/32 3922 ns 3620 ns 191241 rate=8.83886G/s
string_append/45 3935 ns 3633 ns 192302 rate=12.3881G/s
string_append/64 3948 ns 3644 ns 193690 rate=17.5631G/s
string_append/91 4459 ns 4116 ns 170266 rate=22.1066G/s
string_append/128 4470 ns 4126 ns 169312 rate=31.0209G/s
string_append/181 6626 ns 6116 ns 119231 rate=29.5937G/s
string_append/256 5613 ns 5181 ns 134655 rate=49.4099G/s
string_append/362 8021 ns 7404 ns 95064 rate=48.8942G/s
string_append/512 9513 ns 8781 ns 79907 rate=58.3047G/s
string_append/1024 14779 ns 13642 ns 51335 rate=75.0635G/s
```
</details>
Here you can see that `std::vector<char>` _really_ doesn't like chunk sizes different from powers of two, but overall it is still mostly faster than `std::string` (unless it becomes really unlucky).
Again let's look at "the good part" with less influence from weird effects.

This picture is very similar to the one earlier.
Here `std::string` is **70% slower** than `std::vector<char>` regarding `push_back()`;
and around **45% to 80% slower** regarding appending characters in chunks (depending on the chunk size).
Removing memory allocation overhead revealed even larger difference in performance between `std::string` and `std::vector<char>`.
## Quick-bench
Here's another evidence that appending to `std::string` is significantly slower than to `std::vector<char>`:
https://quick-bench.com/q/EKUKhSTDZ3wKF866p1ZiUgRESI8
There it shows appending to `std::string` is from around 16% to two times slower than inserting at the end of `std::vector<char>`.
(Although it uses Clang 17 which is not the most recent, the latest version is not available.)
# For context
I discovered this issue while researching this:
https://github.com/simdjson/simdjson/pull/2408
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJzsXFtz4zaW_jXsF5RYuF8e_CDb7RlXkpnd7qR2a15cEAlJHFOkhgDldn79FgCKBGXT3b3pnkpq0pWKJZH8cHBwrh8gaWurXWPMVcauM3b7Tvdu33ZXru13e9PsqsaY7t2mLZ-vMkyAqzNyXXYZWWdwfdM2tipNVzU7kHFoXek_J2vr_EcZh6CyIMPS9sdja00JXAs2JsMKOF3VbRc_2eumrA0o9rrThTOdzfAN0E0JqsOxNgfTOAvsQdc12PTbrelAe3TVofpVu6ptwNO-KvbA7tu-LsFBPxqgj0fTlF4m14LKAQ02lQPF3uij6YA5mQZs2w4Ug_B6U5vwqet1DWz1qwHtFri9AZ2xfe080DAhL1cKvjhnr9FqWxW6cfUzsHX7ZDrg9roBVWNNFzC1C4MYP9HmAuxkCtd2GbnxSsnI-4zDPIPre-sf6QzQ4Kg7VxV9rTvQGW3bOCe31y4jdxn0q-OX69o0xf6gu8dhMtajgE43YHO-Yr1MVWnAL5u-cT3ANIc0R-DHnz-er_zPxx9B24CKAUQ4hI_g5r9-Ab31s6i1M9aBm1o3O4BhjvI4-D3o_YL7pWobP7fVahzx4VA1D0-6O_THB1cdTEZuYY686lwbl3AQFhzazgDrwhLZNuhrRAFP2oJa902xNyWoq0cD3L6y0TIzDof_4DrP8F14CnxehvmTGVz_nA5pQd22_v8LgxUZvvb_wfWprUoQV_Hh2Nv9w0YXjxmWI1Rc549OO5Nh7ufogmNkwj8OwmpmWOreWwamICPrlzcBsGgx4JSR800Zvsvw3SnvjDXdyWRY3rR94zKspnuG8bz5PzhQgYzcApiRa_8SI_8uPkOuQZxkNRcEgFOeTjTDAmZYTGNczPy2_Vvr_h4d2Ut0mkmTidvw6vw3zDwvvAQ-PrDrDOMuKANn7DYIe4F_E-_NxHV4uCoeCm1dRm7Ktt_UxvtUogZ884qIZ4jw5vHe3jvThZhz35x0V-nGfdDOBBmD4FHY1H7uc5MDDYp93zz6oDKFOB8kYiiJMVD7cOR01ZjOO0sYOkwLQQiDZ1QHEwJj1QTLA-aT9uEx-EQcoLLgn711oG2SYDq6Y9EejtpH3AyvM7yO2vOvgLagM8e2c6YEm-e5k9khvnpxD0Godgs2z84k4pfVqSrjsz4ueEnDqOSmND7OW69scmP7w0F3zxl5_1cfwiobRW_9LJx_UZrcm-BNXXkLunP66DVjPh11U-bBPG4yfDfBDFHupfNlmFRNUfelARm5OXvG-xdXhpC9eOXhVJmn1y4XB-32kwSza5MRDVHHv86nu2PYbPTB2KMuDLhIIGHMh7rytuY1dx2f8snKgRANSu20N40wMoYIE8q4kOrrX3nv-RPkT5A_Qf4E-eYgGcb2dBG_huriIrud7wk1U9kfDs_fo1CKQ9uxsPkWdU_8xE5o4HNljn2rzPkjVTn_phL3j1PffsnqJ0XuH7nCna997GgXFh68uvJpNROm5mua3PYb67oMS-jr3KiQTjc7E9StElV_s-bopeWUuY1rFaDwGnxjUzrlo75OuWnK89xuQJlvzK5q0g-S67_nNurFUs0V9x1Mb6iVv3_YiQMBexls7HcONva3B5tXU80fONwMax7bzt91uLkwmpcW872DjM1HLZXfwGi-c33ybw0fwRbMp2N3rgg_-HF_6mtXHevKdB__1UeWhNwCHJ998citdvpj9asJNzGExwrWmcOx9gohN-75aHyTD65j5x9MeN3tbIblNdiMas7g-vr9327--tP6ww9-uWL5O1xMr7ystJZvOqcYtcrI-_XxWPuKOg4eP-p2GZbTNPAa4FfxXsbZ5ZvOJvf_GjSD66pxnD440LV9U5ry2mzbzhvE-fNZjOyM67vmwqiGO6NVBRj_eBjp2D5lWL6-1t79VzBng9OPIgWT-aolHcXbDBP2l7zxo2CJk2tVzdmnF0Q637f1QSCJX4_hoRcqitD-aowNyH_gveZCjscLOS6vVxfXn_ZV7ZdKnhX5hsh--uRmdI55bLwc6LUZXIz9eemGsPT1SpqLuTDaha4uL0-GnNw1xZuEBQ50Z8Zh9O24QRSI2kj6dm0NXAt2ut8ZcOzaTW0ONh_JzTAkh2DbN0XYSzlbJLDGWdAfQ1SycXsk7ilFdlU3QJdl5Z_RdWQNj603vK7td_v6GWyMezKm8b5hOhs2vZ5a0DZAg7rd6a5y-0NVAFvo2gzy3LUdsO3BnPec3N6AWlsHur4Bu-pkLKiaou06UzhwMNr23XkDrz2YkcO-95KZElhddNW2KqqZgDbdGnMtOFSu2vkJh_l59XN4EUY4zLAaSOcM3yW885mhzTABH8YtMO8m7Pr-oHcmY7cZlnvnjmE3J-TqXeX2_SYv2kOG73prupV2Thf7MJUM32nrdZ_hO2gMLQrJV1uzMSu6VWSlxHazYlIrhCDabvngists-F1f18muVtu7Y-9-ExOewTWGmK2gXGH-M6IZWfsktWY8w9eQZGQN_T0f-qbxdcq4PxY_8xaQYYkh-F9AqKI5BT_99ddA7ds4F__yRhd7M-x_AfAjCv4AqAQ_VL4ikZ8QPLvWjwjcN9Z1fbRfgl-7B4NfmmpbmRJg-DoKme6gTPDplnjHj60uwfpkOr-mZA1gDkNhB3NIhr8yg-vVt_znE-G4cD9XBwPSf15NAIyViQW_WNMNhYvN8_w7SBNCDMBYgMZOgmCoxveEMC4QBKFuI7c0F0Iw8RdvTj6lXRQZEwYjcIZJZcBEFEOK5RkO55AydIF2rkbu0PQ4wpSneAgx5N9zKBUbpZNc5YiJnxbwcIJHJZvhERHwGIJcjeKhnDIF8ZJ8JMHjZKZDxFDAo5wKlOApyRhfwqMJnoJzPMGDPolSBJJJfVjgRfXxZEUxUikchjS8JxQqxiY4heXl2o5wciYdmUvHRJROKEUnW2GM0kU8NC0vpnE1J3F5wCdQEcUnPMkRJYt403yxt9gUj8GAj4XiWJ3xeE4UJ3QJD0_LSyGnKR4RqbxIco7QhIoIVGrRZiYjRDKiIAHlhEah4nDUIZK5IJAtWgxLZIxLTGZzR1IqNGoQYS_cIhqfDJBQEvyNIIEnNIyhIiMahjkSatGa1bi-mFAavA0jKlI3JkTyJLRgIpZXF5_tj3DOg0yEiJkZYh8LzmgkF0osrgKSZ-E4gjCsLWOKpGgIcwonNEw4XPQ0zM62pyBSAU4SCHFqM5IyMcFBSfHiOhA-GAkiQtJoI1hAOIuAzEew0XElg8t4DOGzhgihwVORFAzLuU0LMeEJAvGy50IcLYVSguPKUqggkvOYgJI4xSCZ4t5llzg9wyMaYmm4CjF4jKGcyZxC9tMc69xMpjmDEjWTiEKeeoZAQk0zpjwnkC-hYkCxmiUMojhLwZiE43IwSN8SkQBKmJhLhvEMTMhxvoKyXKFFMAoooRfTRCQFoxyOdqwUzzlFS2AcUCz5fJqMp2AccTJlM4Y4R39ZAJOAkjkYhamkSBCBRZIaBUdyCQwhQIm4UBqdLSdWEiYGjDFXi2jcGweZo80nioREk7sqyjBZQsMEUD7PrxSnCQNxRqYSiuVEEbUoG8GAMj6fKUZsjsbGJCZyphDGS2hpgmBwLiOKYRRxwcRouwjmEqFFvCRFMKhmSZGKGPAQg3hKh4jkHOFFI5mSBOBYzpI28-4ZQh9kcLITlnMllu1kTBNAoLlfcBbDAYIMTh6BVE6UpIt4Y6JAEMFZBFAkBlDBMKEJHIVTzfPCVMZEgRDHqfoQFLFi5gzCqT4muWRqUXtjogCIkXn2ooiF91SxJGOznAmxaMhjngBICZqaIJJ4li2IpGSK7jKHUrJFFU7ZQsFYJVNEKaTzbAGTbEYFmqruiwOWr3fsf4_cQlft9i4yDkWnf30Gbl81Owv2QZrLI7ivnJodjq_dg0I3oGkd2PZ1_exb6VpXoc89du1Gb-rnSEBY44D5VBgTjvj-SOJ54GOtn204R7wDXVubYYOuaqwzugS1cRkWFmzborexfcZe-l3bluGgboYxOLMlZuvO5M43IiGKkqESIblCmOMVRbxYbYwqVuUGSrothSpLHXvkMxO1dFB7neE1lxlmw5Hl4YBgOLj8OUV3Zqe7cjgJPt-8Un6xAy2mmxLoQMwNo1HoR3MtEPDFsBPidOg6PUHZxHOPFpTmfHlQcjwPaSM_JOOJuyizF27cgI2ShaPmw5ZNxuG4czLIPfBK3iRNsCJvgo-Rizro540BZbXdms40Dui6bot4LP2onTNdE2i11zXuh31FqRwGcwN6sLThKHxTGlP6yT2Dsr28w7ajLbZbUDUnYwNl5qdUOXAPTF0dqkY7U4YD8bVrd8btTQc2zyBuS4VDiaYw1uruGVjXdnoXtdcU7cHbnAniTptYg_YKXdd20tKcagNPldu_9ti35OAI2W4g3qwMhnBFN1CsNmKLV6XQJaQbxDDmvw8OTmVk7SPHfxAHRwcODg9_EfmP4eD4BQcnEw6OIzK1tjQXiqvLZvQVDo4SNefgYgGDBOYQoiTdcg6XW-VEO0rNiUKFQ8kiOKU0aQug5Iu9aMrB4TlHiBAJ8nEEhZwqqlxwDBdZhpSDo3SORyI-wxQSlXBmjOMv4uCYmHXwaGCpqBSSpq0GE8usVErCXXTwSLLIYwiKKU6oBgqX5UtZOMbn8x3mTyUSXEztBmNsGS9l4ZC6IAnT9SGUIYYmVIqSMvwtLo7GOhdjklSQBBE08RciRwzCL2HiiGADcSkS2TCEioiJgeNYLrPKKQOHEI0tQVrdciGZmJo_lCNEFmeaNFgERc4XyxlnRilVKm1fuFx0tqS9whDFdkqmXAERSonR1QjNKeOLtpc0V4xH12WYJWuMCEFJ7yJyAuniKiStFYGRjccSzWaqxMTTUJYzjJYtZGysABexEeWYpqwqQmqKAljmSqlFRmrqqwATJDL5WKX0ABJk4qOozIl6I6aMbRVQQs5CnoIk7Vtkqj8Kcyjgooy-uxJUzFyMq7QNQhAhOXX2NEdvxWXfVqHA2qWkpUDpe8bgxHdLmDNMLpvJKW8gyuasPqHJ6jJMyRRVBGU5hYtEEgJUzBs8SmjKSnHIJZ66XJhj8Rb5Rqh4i-IiYvIJquRbkhEPdsFKITTjy5BIyDeRSy7eIN8oRnMwOQPDXIz2oSTMoSRvkG-UzHUG1UxnXCXOmlPO1CJvIQGlEF6wnzOOkfApxqFcccUWeYEkR1CKL1AjaePVlvK9XPFFDilJDiE0zjipaIKIK8r4lBIlw2KRMUs3ahjFczw14AmOaZK8JBOXdHTKwJ3_EYVneITH6SOFfLA6O1YuiZSLeGmKUGTOIvPI2iCFCcTJNg2RcpH1Sbdp1JzjIpzGhKYIVzAJ7Iy_cPzXODhK2cV6IB6XF2I-0Ug4R5AvzjdJFHSWpj1eLHMRVwThJMVCDJfZ2iRV8HmZzAf5BqP2apzqWpUzRb6AigOMx3DC0Mx5CeXT3ihVOYVvsLZTypAwhgQxp7gUg5wmCUgquughExEHFIuySZHKBoRSEyfKpE_cizMdCTiAqIj5BxE-cxSGCJmqMZZDTthX8W-BKXpu-8B4WHM-e_MZDuihM7qunx9A2RrbZFi4-FXpiY-xCVuy7drD_PCR7xE3vQOtbyHrGlQufJneVXUNDq119TPYauvO36V_nVcJnEVtrPWPb0zRHowFUTDQN3VfPD5PdMV6p6tmJPDqtn0E4YzRS_4uMBkRttnWvWkKE2fwZKquBGa7NYWL57a-EbFB8bbQhRSrckvFim7pdqX5lq8oNFCKTckLvo1998_7yoJjVbg-Hv06me4Z2OpQ1boDLn5vvm0MMLqrq_E7wV_CBr5Cy31PNpANbKD8DWxghuUbhOCw9B_MoQ2c18Ec2u45Ze688e2NLkFnTkbXpow_GVHrbme60XwL44c8mm7bdgft356P030V23f5Cw8Tg_bffVU8rs6ckF-rYKK6aQNxZ05VGaQIjvlbf5Xi80R6ZJnmlvyvScTBnP-V4bv3P_zyw_7jz7f_IE8_3EnOj-gf1S-7D-8_3svxJxW8mTpg9-2T_ULZg6sNxoL4YCbuqY1fjP_8T2xc0LBL6sdyXbt92-_2XsDeGjv8tAUS0zfhmzbi-pAEOlOYxvnIFQ9Dhp_DOJnOelsabtYnXdV6U5t8ICLjj3PcxV8hceaTC1_RLytbeOsLRG9lQWVtb4YzuJ2xRnfFPuhp_OWJNyKLrQ7lP23bzF8e-7r2yZJC-a68IqUiSr8zV0gwjhGBnLzbX22QgEYVEG8U0QxqyATdaGQI1oJSVr6rrjDEDErMEWaCkpwWhuMtQ5ARXZQcZxSag67qvK5Ph7ztdu_CVK4QYxTBd7XemNpexS8Q1NXm_M398DWCd92Vf2q16Xc2o7CurLMTjqtcHX6pJnmM3Wbs2ntixm5B8hMpwxnVL7OvQ1_sv9ol7t71XX31xiJ4uYc_q2PX_tMUvtULyvARftDH6Qr_XwAAAP__-7zuFw">