1e718ec51a
The primary reason for this change is to reduce the size of buffers allocated by std::pmr::monotonic_buffer_resource. Previously, a new buffer would always add the size of the linked list node (11 bytes) and then round up to the next power of two. This results in a huge increase if the expected size of the next buffer is already a power of two. For example, if the resource is constructed with a desired initial size of 4096 the first buffer it allocates will be std::bit_ceil(4096+11) which is 8192. If the user has carefully selected the initial size to match their expected memory requirements then allocating double that amount wastes a lot of memory. After this patch the allocated size will be rounded up to a 64-byte boundary, instead of to a power of two. This means for an initial size of 4096 only 4160 bytes get allocated. Previously only the base-2 logarithm of the size was stored, which could be stored in a single 8-bit integer. Now that the size isn't always a power of two we need to use more bits to store it. As the size is always a multiple of 64 the low six bits are not needed, and so we can use the same approach that the pool resources already use of storing the base-2 logarithm of the alignment in the low bits that are not used for the size. To avoid code duplication, a new aligned_size<N> helper class is introduced by this patch, which is then used by both the pool resources' big_block type and the monotonic_buffer_resource::_Chunk type. Originally the big_block type used two bit-fields to store the size and alignment in the space of a single size_t member. The aligned_size type uses a single size_t member and uses masks and bitwise operations to manipulate the size and alignment values. This results in better code than the old version, because the bit-fields weren't optimally ordered for little endian architectures, so the alignment was actually stored in the high bits, not the unused low bits, requiring additional shifts to calculate the values. Using bitwise operations directly avoids needing to reorder the bit-fields depending on the endianness. While adapting the _Chunk and big_block types to use aligned_size<N> I also added checks for size overflows (technically, unsigned wraparound). The memory resources now ensure that when they require an allocation that is too large to represent in size_t they will request SIZE_MAX bytes from the upstream resource, rather than requesting a small value that results from wrapround. The testsuite is enhanced to verify this. libstdc++-v3/ChangeLog: PR libstdc++/96942 * include/std/memory_resource (monotonic_buffer_resource::do_allocate): Use __builtin_expect when checking if a new buffer needs to be allocated from the upstream resource, and for checks for edge cases like zero sized buffers and allocations. * src/c++17/memory_resource.cc (aligned_size): New class template. (aligned_ceil): New helper function to round up to a given alignment. (monotonic_buffer_resource::chunk): Replace _M_size and _M_align with an aligned_size member. Remove _M_canary member. Change _M_next to pointer instead of unaligned buffer. (monotonic_buffer_resource::chunk::allocate): Round up to multiple of 64 instead of to power of two. Check for size overflow. Remove redundant check for minimum required alignment. (monotonic_buffer_resource::chunk::release): Adjust for changes to data members. (monotonic_buffer_resource::_M_new_buffer): Use aligned_ceil. (big_block): Replace _M_size and _M_align with aligned_size member. (big_block::big_block): Check for size overflow. (big_block::size, big_block::align): Adjust to use aligned_size. (big_block::alloc_size): Use aligned_ceil. (munge_options): Use aligned_ceil. (__pool_resource::allocate): Use big_block::align for alignment. * testsuite/20_util/monotonic_buffer_resource/allocate.cc: Check upstream resource gets expected values for impossible sizes. * testsuite/20_util/unsynchronized_pool_resource/allocate.cc: Likewise. Adjust checks for expected alignment in existing test. |
||
---|---|---|
.. | ||
add_const | ||
add_cv | ||
add_lvalue_reference | ||
add_pointer | ||
add_rvalue_reference | ||
add_volatile | ||
addressof | ||
align | ||
aligned_storage | ||
aligned_union | ||
alignment_of | ||
allocator | ||
allocator_traits | ||
any | ||
as_const | ||
assume_aligned | ||
auto_ptr | ||
bad_function_call | ||
bind | ||
bool_constant | ||
common_reference/requirements | ||
common_type/requirements | ||
conditional/requirements | ||
decay/requirements | ||
declval/requirements | ||
default_delete | ||
duration | ||
duration_cast | ||
enable_if/requirements | ||
enable_shared_from_this | ||
exchange | ||
extent | ||
forward | ||
from_chars | ||
function | ||
function_objects | ||
has_unique_object_representations | ||
has_virtual_destructor | ||
hash | ||
headers | ||
in_place | ||
integer_comparisons | ||
integer_sequence | ||
integral_constant | ||
invoke_result | ||
is_abstract | ||
is_aggregate | ||
is_arithmetic | ||
is_array | ||
is_assignable | ||
is_base_of | ||
is_bounded_array | ||
is_class | ||
is_complete_or_unbounded | ||
is_compound | ||
is_const | ||
is_constant_evaluated | ||
is_constructible | ||
is_convertible | ||
is_copy_assignable | ||
is_copy_constructible | ||
is_default_constructible | ||
is_destructible | ||
is_empty | ||
is_enum | ||
is_final | ||
is_floating_point | ||
is_function | ||
is_fundamental | ||
is_implicitly_default_constructible | ||
is_integral | ||
is_invocable | ||
is_literal_type | ||
is_lvalue_reference | ||
is_member_function_pointer | ||
is_member_object_pointer | ||
is_member_pointer | ||
is_move_assignable | ||
is_move_constructible | ||
is_nothrow_assignable | ||
is_nothrow_constructible | ||
is_nothrow_convertible | ||
is_nothrow_copy_assignable | ||
is_nothrow_copy_constructible | ||
is_nothrow_default_constructible | ||
is_nothrow_destructible | ||
is_nothrow_invocable | ||
is_nothrow_move_assignable | ||
is_nothrow_move_constructible | ||
is_nothrow_swappable | ||
is_nothrow_swappable_with | ||
is_null_pointer | ||
is_object | ||
is_pod | ||
is_pointer | ||
is_polymorphic | ||
is_reference | ||
is_rvalue_reference | ||
is_same | ||
is_scalar | ||
is_signed | ||
is_standard_layout | ||
is_swappable | ||
is_swappable_with | ||
is_trivial | ||
is_trivially_assignable | ||
is_trivially_constructible | ||
is_trivially_copy_assignable | ||
is_trivially_copy_constructible | ||
is_trivially_copyable | ||
is_trivially_default_constructible | ||
is_trivially_destructible | ||
is_trivially_move_assignable | ||
is_trivially_move_constructible | ||
is_unbounded_array | ||
is_union | ||
is_unsigned | ||
is_void | ||
is_volatile | ||
logical_traits | ||
make_signed/requirements | ||
make_unsigned/requirements | ||
memory_resource | ||
monotonic_buffer_resource | ||
move | ||
move_if_noexcept | ||
nonesuch | ||
optional | ||
owner_less | ||
pair | ||
pointer_safety | ||
pointer_traits | ||
polymorphic_allocator | ||
rank | ||
ratio | ||
raw_storage_iterator | ||
reference_wrapper | ||
remove_all_extents | ||
remove_const | ||
remove_cv | ||
remove_cvref | ||
remove_extent | ||
remove_pointer | ||
remove_reference | ||
remove_volatile | ||
result_of | ||
scoped_allocator | ||
shared_ptr | ||
specialized_algorithms | ||
steady_clock | ||
synchronized_pool_resource | ||
system_clock | ||
time_point | ||
time_point_cast | ||
to_address | ||
to_chars | ||
tuple | ||
type_identity/requirements | ||
typeindex | ||
underlying_type/requirements | ||
unique_ptr | ||
unsynchronized_pool_resource | ||
unwrap_reference | ||
uses_allocator | ||
variant | ||
void_t | ||
weak_ptr | ||
rel_ops.cc | ||
temporary_buffer.cc | ||
variable_templates_for_traits.cc |