Skip to content

Commit 20f39bf

Browse files
authored
Lower std::string's alignment requirement from 16 to 8. (#68807)
This allows smaller allocations to occur, closer to the actual std::string's required size. This is particularly effective in decreasing the allocation size upon initial construction (where __recommend is called to determine the size). Although the memory savings per-string are never more than 8 bytes per string initially, this quickly adds up. And has lead to not insigficant memory savings at Google. Unfortunately, this change is ABI breaking because it changes the value returned by max_size. So it has to be guarded.
1 parent 160e8eb commit 20f39bf

File tree

5 files changed

+72
-2
lines changed

5 files changed

+72
-2
lines changed

libcxx/docs/ReleaseNotes/18.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ ABI Affecting Changes
133133
results in an ABI break, however in practice we expect uses of ``std::projected`` in ABI-sensitive places to be
134134
extremely rare. Any error resulting from this change should result in a link-time error.
135135

136+
- Under the unstable ABI, the internal alignment requirements for heap allocations
137+
inside ``std::string`` has decreased from 16 to 8 This save memory since string requests fewer additional
138+
bytes than it did previously. However, this also changes the return value of ``std::string::max_size``
139+
and can cause code compiled against older libc++ versions but linked at runtime to a new version
140+
to throw a different exception when attempting allocations that are too large
141+
(``std::bad_alloc`` vs ``std::length_error``).
142+
136143
Build System Changes
137144
--------------------
138145

libcxx/include/__config

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@
167167
// The implementation moved to the header, but we still export the symbols from
168168
// the dylib for backwards compatibility.
169169
# define _LIBCPP_ABI_DO_NOT_EXPORT_TO_CHARS_BASE_10
170+
// Save memory by providing the allocator more freedom to allocate the most
171+
// efficient size class by dropping the alignment requirements for std::string's
172+
// pointer from 16 to 8. This changes the output of std::string::max_size,
173+
// which makes it ABI breaking
174+
# define _LIBCPP_ABI_STRING_8_BYTE_ALIGNMENT
170175
# elif _LIBCPP_ABI_VERSION == 1
171176
# if !(defined(_LIBCPP_OBJECT_FORMAT_COFF) || defined(_LIBCPP_OBJECT_FORMAT_XCOFF))
172177
// Enable compiling copies of now inline methods into the dylib to support

libcxx/include/string

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1851,7 +1851,14 @@ private:
18511851
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
18521852
size_type __align_it(size_type __s) _NOEXCEPT
18531853
{return (__s + (__a-1)) & ~(__a-1);}
1854-
enum {__alignment = 16};
1854+
enum {
1855+
__alignment =
1856+
#ifdef _LIBCPP_ABI_STRING_8_BYTE_ALIGNMENT
1857+
8
1858+
#else
1859+
16
1860+
#endif
1861+
};
18551862
static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
18561863
size_type __recommend(size_type __s) _NOEXCEPT
18571864
{
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// <string>
10+
11+
// This test demonstrates the smaller allocation sizes when the alignment
12+
// requirements of std::string are dropped from 16 to 8.
13+
#include <algorithm>
14+
#include <cassert>
15+
#include <cstddef>
16+
#include <string>
17+
18+
#include "test_macros.h"
19+
20+
// alignment of the string heap buffer is hardcoded to either 16 or 8
21+
22+
const std::size_t alignment =
23+
#ifdef _LIBCPP_ABI_STRING_8_BYTE_ALIGNMENT
24+
8;
25+
#else
26+
16;
27+
#endif
28+
29+
int main(int, char**) {
30+
std::string input_string;
31+
input_string.resize(64, 'a');
32+
33+
// Call a constructor which selects its size using __recommend.
34+
std::string test_string(input_string.data());
35+
const std::size_t expected_align8_size = 71;
36+
37+
// Demonstrate the lesser capacity/allocation size when the alignment requirement is 8.
38+
if (alignment == 8) {
39+
assert(test_string.capacity() == expected_align8_size);
40+
} else {
41+
assert(test_string.capacity() == expected_align8_size + 8);
42+
}
43+
44+
return 0;
45+
}

libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@
1818
#include "test_macros.h"
1919

2020
// alignment of the string heap buffer is hardcoded to 16
21-
static const std::size_t alignment = 16;
21+
22+
static const std::size_t alignment =
23+
#ifdef _LIBCPP_ABI_STRING_8_BYTE_ALIGNMENT
24+
8;
25+
#else
26+
16;
27+
#endif
2228

2329
template <class = int>
2430
TEST_CONSTEXPR_CXX20 void full_size() {

0 commit comments

Comments
 (0)