STL/stl/inc/xstring

3491 строка
156 KiB
C++
Исходник Обычный вид История

2019-09-05 01:57:56 +03:00
// xstring internal header
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#ifndef _XSTRING_
#define _XSTRING_
#include <yvals_core.h>
#if _STL_COMPILER_PREPROCESSOR
2023-08-11 03:44:26 +03:00
#include <__msvc_sanitizer_annotate_container.hpp>
#include <__msvc_string_view.hpp>
2019-09-05 01:57:56 +03:00
#include <iosfwd>
#include <xmemory>
#if _HAS_CXX17
#include <xpolymorphic_allocator.h>
#endif // _HAS_CXX17
#pragma pack(push, _CRT_PACKING)
#pragma warning(push, _STL_WARNING_LEVEL)
#pragma warning(disable : _STL_DISABLED_WARNINGS)
_STL_DISABLE_CLANG_WARNINGS
#pragma push_macro("new")
#undef new
_STD_BEGIN
template <class _Mystr>
class _String_const_iterator : public _Iterator_base {
public:
#if _HAS_CXX20
using iterator_concept = contiguous_iterator_tag;
#endif // _HAS_CXX20
2019-09-05 01:57:56 +03:00
using iterator_category = random_access_iterator_tag;
using value_type = typename _Mystr::value_type;
using difference_type = typename _Mystr::difference_type;
using pointer = typename _Mystr::const_pointer;
using reference = const value_type&;
2019-09-05 01:57:56 +03:00
_CONSTEXPR20 _String_const_iterator() noexcept : _Ptr() {}
2019-09-05 01:57:56 +03:00
_CONSTEXPR20 _String_const_iterator(pointer _Parg, const _Container_base* _Pstring) noexcept : _Ptr(_Parg) {
2019-09-05 01:57:56 +03:00
this->_Adopt(_Pstring);
}
_NODISCARD _CONSTEXPR20 reference operator*() const noexcept {
2019-09-05 01:57:56 +03:00
#if _ITERATOR_DEBUG_LEVEL >= 1
_STL_VERIFY(_Ptr, "cannot dereference value-initialized string iterator");
const auto _Mycont = static_cast<const _Mystr*>(this->_Getcont());
_STL_VERIFY(_Mycont, "cannot dereference string iterator because the iterator was "
"invalidated (e.g. reallocation occurred, or the string was destroyed)");
2019-09-05 01:57:56 +03:00
const auto _Contptr = _Mycont->_Myptr();
const auto _Rawptr = _Unfancy(_Ptr);
_STL_VERIFY(_Contptr <= _Rawptr && _Rawptr < _Contptr + _Mycont->_Mysize,
"cannot dereference string iterator because it is out of range (e.g. an end iterator)");
#endif // _ITERATOR_DEBUG_LEVEL >= 1
_Analysis_assume_(_Ptr);
return *_Ptr;
}
_NODISCARD _CONSTEXPR20 pointer operator->() const noexcept {
2019-09-05 01:57:56 +03:00
return pointer_traits<pointer>::pointer_to(**this);
}
_CONSTEXPR20 _String_const_iterator& operator++() noexcept {
2019-09-05 01:57:56 +03:00
#if _ITERATOR_DEBUG_LEVEL >= 1
_STL_VERIFY(_Ptr, "cannot increment value-initialized string iterator");
const auto _Mycont = static_cast<const _Mystr*>(this->_Getcont());
_STL_VERIFY(_Mycont, "cannot increment string iterator because the iterator was "
"invalidated (e.g. reallocation occurred, or the string was destroyed)");
2019-09-05 01:57:56 +03:00
_STL_VERIFY(_Unfancy(_Ptr) < _Mycont->_Myptr() + _Mycont->_Mysize, "cannot increment string iterator past end");
#endif // _ITERATOR_DEBUG_LEVEL >= 1
++_Ptr;
return *this;
}
_CONSTEXPR20 _String_const_iterator operator++(int) noexcept {
2019-09-05 01:57:56 +03:00
_String_const_iterator _Tmp = *this;
++*this;
return _Tmp;
}
_CONSTEXPR20 _String_const_iterator& operator--() noexcept {
2019-09-05 01:57:56 +03:00
#if _ITERATOR_DEBUG_LEVEL >= 1
_STL_VERIFY(_Ptr, "cannot decrement value-initialized string iterator");
const auto _Mycont = static_cast<const _Mystr*>(this->_Getcont());
_STL_VERIFY(_Mycont, "cannot decrement string iterator because the iterator was "
"invalidated (e.g. reallocation occurred, or the string was destroyed)");
2019-09-05 01:57:56 +03:00
_STL_VERIFY(_Mycont->_Myptr() < _Unfancy(_Ptr), "cannot decrement string iterator before begin");
#endif // _ITERATOR_DEBUG_LEVEL >= 1
--_Ptr;
return *this;
}
_CONSTEXPR20 _String_const_iterator operator--(int) noexcept {
2019-09-05 01:57:56 +03:00
_String_const_iterator _Tmp = *this;
--*this;
return _Tmp;
}
_CONSTEXPR20 void _Verify_offset(const difference_type _Off) const noexcept {
2019-09-05 01:57:56 +03:00
#if _ITERATOR_DEBUG_LEVEL >= 1
if (_Off == 0) {
return;
}
_STL_ASSERT(_Ptr, "cannot seek value-initialized string iterator");
const auto _Mycont = static_cast<const _Mystr*>(this->_Getcont());
_STL_ASSERT(_Mycont, "cannot seek string iterator because the iterator was "
"invalidated (e.g. reallocation occurred, or the string was destroyed)");
2019-09-05 01:57:56 +03:00
const auto _Contptr = _Mycont->_Myptr();
const auto _Rawptr = _Unfancy(_Ptr);
if (_Off < 0) {
_STL_VERIFY(_Contptr - _Rawptr <= _Off, "cannot seek string iterator before begin");
}
if (_Off > 0) {
using _Size_type = typename _Mystr::size_type;
const auto _Left = _Mycont->_Mysize - static_cast<_Size_type>(_Rawptr - _Contptr);
_STL_VERIFY(static_cast<_Size_type>(_Off) <= _Left, "cannot seek string iterator after end");
}
#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv
2019-09-05 01:57:56 +03:00
(void) _Off;
#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^
2019-09-05 01:57:56 +03:00
}
_CONSTEXPR20 _String_const_iterator& operator+=(const difference_type _Off) noexcept {
2019-09-05 01:57:56 +03:00
#if _ITERATOR_DEBUG_LEVEL >= 1
_Verify_offset(_Off);
#endif // _ITERATOR_DEBUG_LEVEL >= 1
_Ptr += _Off;
return *this;
}
_NODISCARD _CONSTEXPR20 _String_const_iterator operator+(const difference_type _Off) const noexcept {
2019-09-05 01:57:56 +03:00
_String_const_iterator _Tmp = *this;
_Tmp += _Off;
return _Tmp;
2019-09-05 01:57:56 +03:00
}
_NODISCARD friend _CONSTEXPR20 _String_const_iterator operator+(
const difference_type _Off, _String_const_iterator _Next) noexcept {
_Next += _Off;
return _Next;
}
_CONSTEXPR20 _String_const_iterator& operator-=(const difference_type _Off) noexcept {
2019-09-05 01:57:56 +03:00
return *this += -_Off;
}
_NODISCARD _CONSTEXPR20 _String_const_iterator operator-(const difference_type _Off) const noexcept {
2019-09-05 01:57:56 +03:00
_String_const_iterator _Tmp = *this;
_Tmp -= _Off;
return _Tmp;
2019-09-05 01:57:56 +03:00
}
_NODISCARD _CONSTEXPR20 difference_type operator-(const _String_const_iterator& _Right) const noexcept {
2019-09-05 01:57:56 +03:00
_Compat(_Right);
return static_cast<difference_type>(_Ptr - _Right._Ptr);
2019-09-05 01:57:56 +03:00
}
_NODISCARD _CONSTEXPR20 reference operator[](const difference_type _Off) const noexcept {
2019-09-05 01:57:56 +03:00
return *(*this + _Off);
}
_NODISCARD _CONSTEXPR20 bool operator==(const _String_const_iterator& _Right) const noexcept {
2019-09-05 01:57:56 +03:00
_Compat(_Right);
return _Ptr == _Right._Ptr;
}
#if _HAS_CXX20
_NODISCARD constexpr strong_ordering operator<=>(const _String_const_iterator& _Right) const noexcept {
_Compat(_Right);
return _STD _Unfancy_maybe_null(_Ptr) <=> _STD _Unfancy_maybe_null(_Right._Ptr);
}
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
_NODISCARD bool operator!=(const _String_const_iterator& _Right) const noexcept {
2019-09-05 01:57:56 +03:00
return !(*this == _Right);
}
_NODISCARD bool operator<(const _String_const_iterator& _Right) const noexcept {
2019-09-05 01:57:56 +03:00
_Compat(_Right);
return _Ptr < _Right._Ptr;
}
_NODISCARD bool operator>(const _String_const_iterator& _Right) const noexcept {
2019-09-05 01:57:56 +03:00
return _Right < *this;
}
_NODISCARD bool operator<=(const _String_const_iterator& _Right) const noexcept {
2019-09-05 01:57:56 +03:00
return !(_Right < *this);
}
_NODISCARD bool operator>=(const _String_const_iterator& _Right) const noexcept {
2019-09-05 01:57:56 +03:00
return !(*this < _Right);
}
#endif // !_HAS_CXX20
2019-09-05 01:57:56 +03:00
_CONSTEXPR20 void _Compat(const _String_const_iterator& _Right) const noexcept {
// test for compatible iterator pair
2019-09-05 01:57:56 +03:00
#if _ITERATOR_DEBUG_LEVEL >= 1
_STL_VERIFY(this->_Getcont() == _Right._Getcont(),
"string iterators incompatible (e.g. point to different string instances)");
#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv
2019-09-05 01:57:56 +03:00
(void) _Right;
#endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^
2019-09-05 01:57:56 +03:00
}
#if _ITERATOR_DEBUG_LEVEL >= 1
friend _CONSTEXPR20 void _Verify_range(
const _String_const_iterator& _First, const _String_const_iterator& _Last) noexcept {
2019-09-05 01:57:56 +03:00
_STL_VERIFY(_First._Getcont() == _Last._Getcont(), "string iterators in range are from different containers");
_STL_VERIFY(_First._Ptr <= _Last._Ptr, "string iterator range transposed");
}
#endif // _ITERATOR_DEBUG_LEVEL >= 1
using _Prevent_inheriting_unwrap = _String_const_iterator;
_NODISCARD _CONSTEXPR20 const value_type* _Unwrapped() const noexcept {
return _STD _Unfancy_maybe_null(_Ptr);
2019-09-05 01:57:56 +03:00
}
_CONSTEXPR20 void _Seek_to(const value_type* _It) noexcept {
_Ptr = _STD _Refancy_maybe_null<pointer>(const_cast<value_type*>(_It));
2019-09-05 01:57:56 +03:00
}
pointer _Ptr; // pointer to element in string
};
#if _HAS_CXX20
template <class _Mystr>
struct pointer_traits<_String_const_iterator<_Mystr>> {
using pointer = _String_const_iterator<_Mystr>;
using element_type = const pointer::value_type;
using difference_type = pointer::difference_type;
_NODISCARD static constexpr element_type* to_address(const pointer _Iter) noexcept {
#if _ITERATOR_DEBUG_LEVEL >= 1
const auto _Mycont = static_cast<const _Mystr*>(_Iter._Getcont());
if (!_Mycont) {
_STL_VERIFY(!_Iter._Ptr, "cannot convert string iterator to pointer because the iterator was invalidated "
"(e.g. reallocation occurred, or the string was destroyed)");
}
#endif // _ITERATOR_DEBUG_LEVEL >= 1
const auto _Rawptr = _STD to_address(_Iter._Ptr);
#if _ITERATOR_DEBUG_LEVEL >= 1
if (_Mycont) {
const auto _Contptr = _Mycont->_Myptr();
_STL_VERIFY(_Contptr <= _Rawptr && _Rawptr <= _Contptr + _Mycont->_Mysize,
"cannot convert string iterator to pointer because it is out of range");
}
#endif // _ITERATOR_DEBUG_LEVEL >= 1
return _Rawptr;
}
};
#endif // _HAS_CXX20
2019-09-05 01:57:56 +03:00
template <class _Mystr>
class _String_iterator : public _String_const_iterator<_Mystr> {
public:
using _Mybase = _String_const_iterator<_Mystr>;
2019-09-05 01:57:56 +03:00
#if _HAS_CXX20
using iterator_concept = contiguous_iterator_tag;
#endif // _HAS_CXX20
using iterator_category = random_access_iterator_tag;
using value_type = typename _Mystr::value_type;
using difference_type = typename _Mystr::difference_type;
using pointer = typename _Mystr::pointer;
using reference = value_type&;
2019-09-05 01:57:56 +03:00
using _Mybase::_Mybase;
_NODISCARD _CONSTEXPR20 reference operator*() const noexcept {
2019-09-05 01:57:56 +03:00
return const_cast<reference>(_Mybase::operator*());
}
_NODISCARD _CONSTEXPR20 pointer operator->() const noexcept {
2019-09-05 01:57:56 +03:00
return pointer_traits<pointer>::pointer_to(**this);
}
_CONSTEXPR20 _String_iterator& operator++() noexcept {
2019-09-05 01:57:56 +03:00
_Mybase::operator++();
return *this;
}
_CONSTEXPR20 _String_iterator operator++(int) noexcept {
2019-09-05 01:57:56 +03:00
_String_iterator _Tmp = *this;
_Mybase::operator++();
return _Tmp;
}
_CONSTEXPR20 _String_iterator& operator--() noexcept {
2019-09-05 01:57:56 +03:00
_Mybase::operator--();
return *this;
}
_CONSTEXPR20 _String_iterator operator--(int) noexcept {
2019-09-05 01:57:56 +03:00
_String_iterator _Tmp = *this;
_Mybase::operator--();
return _Tmp;
}
_CONSTEXPR20 _String_iterator& operator+=(const difference_type _Off) noexcept {
2019-09-05 01:57:56 +03:00
_Mybase::operator+=(_Off);
return *this;
}
_NODISCARD _CONSTEXPR20 _String_iterator operator+(const difference_type _Off) const noexcept {
2019-09-05 01:57:56 +03:00
_String_iterator _Tmp = *this;
_Tmp += _Off;
return _Tmp;
2019-09-05 01:57:56 +03:00
}
_NODISCARD friend _CONSTEXPR20 _String_iterator operator+(
const difference_type _Off, _String_iterator _Next) noexcept {
_Next += _Off;
return _Next;
}
_CONSTEXPR20 _String_iterator& operator-=(const difference_type _Off) noexcept {
2019-09-05 01:57:56 +03:00
_Mybase::operator-=(_Off);
return *this;
}
using _Mybase::operator-;
_NODISCARD _CONSTEXPR20 _String_iterator operator-(const difference_type _Off) const noexcept {
2019-09-05 01:57:56 +03:00
_String_iterator _Tmp = *this;
_Tmp -= _Off;
return _Tmp;
2019-09-05 01:57:56 +03:00
}
_NODISCARD _CONSTEXPR20 reference operator[](const difference_type _Off) const noexcept {
2019-09-05 01:57:56 +03:00
return const_cast<reference>(_Mybase::operator[](_Off));
}
using _Prevent_inheriting_unwrap = _String_iterator;
_NODISCARD _CONSTEXPR20 value_type* _Unwrapped() const noexcept {
return const_cast<value_type*>(_STD _Unfancy_maybe_null(this->_Ptr));
2019-09-05 01:57:56 +03:00
}
};
#if _HAS_CXX20
template <class _Mystr>
struct pointer_traits<_String_iterator<_Mystr>> {
using pointer = _String_iterator<_Mystr>;
using element_type = pointer::value_type;
using difference_type = pointer::difference_type;
_NODISCARD static constexpr element_type* to_address(const pointer _Iter) noexcept {
#if _ITERATOR_DEBUG_LEVEL >= 1
const auto _Mycont = static_cast<const _Mystr*>(_Iter._Getcont());
if (!_Mycont) {
_STL_VERIFY(!_Iter._Ptr, "cannot convert string iterator to pointer because the iterator was invalidated "
"(e.g. reallocation occurred, or the string was destroyed)");
}
#endif // _ITERATOR_DEBUG_LEVEL >= 1
const auto _Rawptr = _STD to_address(_Iter._Ptr);
#if _ITERATOR_DEBUG_LEVEL >= 1
if (_Mycont) {
const auto _Contptr = _Mycont->_Myptr();
_STL_VERIFY(_Contptr <= _Rawptr && _Rawptr <= _Contptr + _Mycont->_Mysize,
"cannot convert string iterator to pointer because it is out of range");
}
#endif // _ITERATOR_DEBUG_LEVEL >= 1
return const_cast<element_type*>(_Rawptr);
}
};
#endif // _HAS_CXX20
template <class _Value_type, class _Size_type, class _Difference_type, class _Pointer, class _Const_pointer>
2019-09-05 01:57:56 +03:00
struct _String_iter_types {
using value_type = _Value_type;
using size_type = _Size_type;
using difference_type = _Difference_type;
using pointer = _Pointer;
using const_pointer = _Const_pointer;
};
template <class _Val_types>
class _String_val : public _Container_base {
public:
using value_type = typename _Val_types::value_type;
using size_type = typename _Val_types::size_type;
using difference_type = typename _Val_types::difference_type;
using pointer = typename _Val_types::pointer;
using const_pointer = typename _Val_types::const_pointer;
using reference = value_type&;
using const_reference = const value_type&;
_CONSTEXPR20 _String_val() noexcept : _Bx() {}
2019-09-05 01:57:56 +03:00
// length of internal buffer, [1, 16] (NB: used by the debugger visualizer)
2019-09-05 01:57:56 +03:00
static constexpr size_type _BUF_SIZE = 16 / sizeof(value_type) < 1 ? 1 : 16 / sizeof(value_type);
// roundup mask for allocated buffers, [0, 15]
static constexpr size_type _Alloc_mask = sizeof(value_type) <= 1 ? 15
: sizeof(value_type) <= 2 ? 7
: sizeof(value_type) <= 4 ? 3
: sizeof(value_type) <= 8 ? 1
: 0;
// capacity in small mode
static constexpr size_type _Small_string_capacity = _BUF_SIZE - 1;
2019-09-05 01:57:56 +03:00
_NODISCARD _CONSTEXPR20 value_type* _Myptr() noexcept {
2019-09-05 01:57:56 +03:00
value_type* _Result = _Bx._Buf;
if (_Large_mode_engaged()) {
2019-09-05 01:57:56 +03:00
_Result = _Unfancy(_Bx._Ptr);
}
return _Result;
}
_NODISCARD _CONSTEXPR20 const value_type* _Myptr() const noexcept {
2019-09-05 01:57:56 +03:00
const value_type* _Result = _Bx._Buf;
if (_Large_mode_engaged()) {
2019-09-05 01:57:56 +03:00
_Result = _Unfancy(_Bx._Ptr);
}
return _Result;
}
_NODISCARD _CONSTEXPR20 bool _Large_mode_engaged() const noexcept {
return _Myres > _Small_string_capacity;
}
_CONSTEXPR20 void _Activate_SSO_buffer() noexcept {
// start the lifetime of the array elements
#if _HAS_CXX20
if (_STD is_constant_evaluated()) {
for (size_type _Idx = 0; _Idx < _BUF_SIZE; ++_Idx) {
_Bx._Buf[_Idx] = value_type();
}
}
#endif // _HAS_CXX20
2019-09-05 01:57:56 +03:00
}
_CONSTEXPR20 void _Check_offset(const size_type _Off) const {
// checks whether _Off is in the bounds of [0, size()]
2019-09-05 01:57:56 +03:00
if (_Mysize < _Off) {
_Xran();
}
}
_CONSTEXPR20 void _Check_offset_exclusive(const size_type _Off) const {
// checks whether _Off is in the bounds of [0, size())
2019-09-05 01:57:56 +03:00
if (_Mysize <= _Off) {
_Xran();
}
}
[[noreturn]] static void _Xran() {
_Xout_of_range("invalid string position");
}
_NODISCARD _CONSTEXPR20 size_type _Clamp_suffix_size(const size_type _Off, const size_type _Size) const noexcept {
2019-09-05 01:57:56 +03:00
// trims _Size to the longest it can be assuming a string at/after _Off
return (_STD min)(_Size, _Mysize - _Off);
2019-09-05 01:57:56 +03:00
}
union _Bxty { // storage for small buffer or pointer to larger one
// This constructor previously initialized _Ptr. Don't rely on the new behavior without
// renaming `_String_val` (and fixing the visualizer).
_CONSTEXPR20 _Bxty() noexcept : _Buf() {} // user-provided, for fancy pointers
_CONSTEXPR20 ~_Bxty() noexcept {} // user-provided, for fancy pointers
2019-09-05 01:57:56 +03:00
value_type _Buf[_BUF_SIZE];
pointer _Ptr;
char _Alias[_BUF_SIZE]; // TRANSITION, ABI: _Alias is preserved for binary compatibility (especially /clr)
_CONSTEXPR20 void _Switch_to_buf() noexcept {
_STD _Destroy_in_place(_Ptr);
#if _HAS_CXX20
// start the lifetime of the array elements
if (_STD is_constant_evaluated()) {
for (size_type _Idx = 0; _Idx < _BUF_SIZE; ++_Idx) {
_Buf[_Idx] = value_type();
}
}
#endif // _HAS_CXX20
}
};
_Bxty _Bx;
2019-09-05 01:57:56 +03:00
// invariant: _Myres >= _Mysize, and _Myres >= _Small_string_capacity (after string's construction)
// neither _Mysize nor _Myres takes account of the extra null terminator
size_type _Mysize = 0; // current length of string (size)
size_type _Myres = 0; // current storage reserved for string (capacity)
2019-09-05 01:57:56 +03:00
};
// get _Ty's size after being EBCO'd
2019-09-05 01:57:56 +03:00
template <class _Ty>
constexpr size_t _Size_after_ebco_v = is_empty_v<_Ty> ? 0 : sizeof(_Ty);
2019-09-05 01:57:56 +03:00
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
struct _String_constructor_concat_tag {
// tag to select constructors used by basic_string's concatenation operators (operator+)
explicit _String_constructor_concat_tag() = default;
};
struct _String_constructor_rvalue_allocator_tag {
// tag to select constructors used by basic_stringbuf's rvalue str()
explicit _String_constructor_rvalue_allocator_tag() = default;
};
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
[[noreturn]] inline void _Xlen_string() {
_Xlength_error("string too long");
}
#if _HAS_CXX23
template <class _Rng, class _Ty>
concept _Contiguous_range_of =
Toolset update: VS 2022 17.7 Preview 1 (#3711) * PowerShell 7.3.4. * New pool. * VS 2022 17.7 Preview 1. * Require Clang 16. * Remove workaround for VSO-1753916 'C++23 tuple causes x86chk assertion: !"If lookup found RDSymbol before it should find something this time, too", ParseTree.cpp 10376'. * Remove workarounds for VSO-1799670 "EDG rejects constexpr vector in debug mode with 'attempt to access expired storage' errors, part 3". * Remove workarounds for LLVM-44833 "[concepts] deferred substitution into requirements of class template members not implemented". * [llvm16] Clang has implemented P0960; unskip tests that now pass, and remove workaround in `P2474R2_views_repeat`. * [llvm16] Clang now warns about overriding `/fp:strict` with `-ffp-contract`. Update `floating_point_model_matrix.lst` to silence these warnings. * [llvm16] Fix redeclaration of `adjacent_transform_view` (atomic constraint expressions effectively cannot be reproduced since each utterance is distinct). * [llvm16] Disambiguate `tuple` comparison operators. * [llvm16] Workaround LLVM-62290, which has been fixed for Clang 17, in `<ranges>`. * [llvm16] Remove workarounds for a couple of `TRANSITION, Clang 16` issues in `<ranges>` and `<type_traits>`. * [llvm16] Perma-workaround unfiled clang bug in `P0896R4_views_take`. * [llvm16 followup] Improve arrow comments. * [llvm16 followup] We `_EXPORT_STD` both declarations and definitions. * Disable UBSan to work around GH 3568 "Investigate why Clang/LLVM UBSan doesn't link". * Guard `_addcarry_u64`/`_subborrow_u64` for Clang. * `LDBL_DECIMAL_DIG` is now missing for Clang too. * Although LLVM-46207 is still open, Clang now likes tgmath.h. * Cite LLVM-62096 "Clang complains about concept depending on itself". * Add workaround for LLVM-62762 "32-bit-only misoptimization of printf calls on Windows". * clang-format 16, no manual changes. The changes are almost entirely positive, with no horrible mangling, and limited weirdness around `&`, `&&`, and `*`. No need for manual fixups. * .clang-format: Update link to Clang 16. * .clang-format: Sort defaults, no behavioral changes. * .clang-format: Drop commented-out defaults, no behavioral changes. `BreakBeforeInheritanceComma` and `BreakConstructorInitializersBeforeComma` were never documented and aren't printed out by `-dump-config` anymore. The weird empty `BasedOnStyle` isn't printed out by `-dump-config` anymore. `ConstructorInitializerAllOnOneLineOrOnePerLine` and `AllowAllConstructorInitializersOnNextLine` are now deprecated and aren't printed out by `-dump-config` anymore. * .clang-format: `DeriveLineEnding` and `UseCRLF` were deprecated and fused into `LineEnding`, no behavioral changes. * .clang-format: `AlignTrailingComments` is no longer a bool. Use the new options, no behavioral changes. * .clang-format: `SortUsingDeclarations` (which we didn't customize) is no longer a bool, no behavioral changes. * .clang-format: Add new defaults, no behavioral changes. * .clang-format: `InsertNewlineAtEOF` is new, set it to `true`. Doesn't change any files right now. * .clang-format: `RemoveSemicolon` is new, set it to `true`. * clang-format 16, no manual changes - remove semicolons. --------- Co-authored-by: Casey Carter <Casey@Carter.net>
2023-05-19 00:14:16 +03:00
(_RANGES contiguous_range<_Rng>) &&same_as<remove_cvref_t<_RANGES range_reference_t<_Rng>>, _Ty>;
#endif // _HAS_CXX23
_EXPORT_STD template <class _Elem, class _Traits = char_traits<_Elem>, class _Alloc = allocator<_Elem>>
2019-09-05 01:57:56 +03:00
class basic_string { // null-terminated transparent array of elements
private:
friend _Tidy_deallocate_guard<basic_string>;
friend basic_stringbuf<_Elem, _Traits, _Alloc>;
2019-09-05 01:57:56 +03:00
using _Alty = _Rebind_alloc_t<_Alloc, _Elem>;
using _Alty_traits = allocator_traits<_Alty>;
using _Scary_val = _String_val<conditional_t<_Is_simple_alloc_v<_Alty>, _Simple_types<_Elem>,
_String_iter_types<_Elem, typename _Alty_traits::size_type, typename _Alty_traits::difference_type,
typename _Alty_traits::pointer, typename _Alty_traits::const_pointer>>>;
2019-09-05 01:57:56 +03:00
static_assert(!_ENFORCE_MATCHING_ALLOCATORS || is_same_v<_Elem, typename _Alloc::value_type>,
_MISMATCHED_ALLOCATOR_MESSAGE("basic_string<T, Traits, Allocator>", "T"));
static_assert(is_same_v<_Elem, typename _Traits::char_type>,
"N4950 [string.require]/3 requires that the supplied "
2019-09-05 01:57:56 +03:00
"char_traits character type match the string's character type.");
static_assert(!is_array_v<_Elem> && is_trivial_v<_Elem> && is_standard_layout_v<_Elem>,
"The character type of basic_string must be a non-array trivial standard-layout type. See N4950 "
"[strings.general]/1.");
2019-09-05 01:57:56 +03:00
public:
using traits_type = _Traits;
using allocator_type = _Alloc;
using value_type = _Elem;
using size_type = typename _Alty_traits::size_type;
using difference_type = typename _Alty_traits::difference_type;
using pointer = typename _Alty_traits::pointer;
using const_pointer = typename _Alty_traits::const_pointer;
using reference = value_type&;
using const_reference = const value_type&;
using iterator = _String_iterator<_Scary_val>;
using const_iterator = _String_const_iterator<_Scary_val>;
using reverse_iterator = _STD reverse_iterator<iterator>;
using const_reverse_iterator = _STD reverse_iterator<const_iterator>;
private:
static constexpr size_type _BUF_SIZE = _Scary_val::_BUF_SIZE;
static constexpr size_type _Alloc_mask = _Scary_val::_Alloc_mask;
static constexpr size_type _Small_string_capacity = _Scary_val::_Small_string_capacity;
// least real allocation size, including space reserved for terminating null
static constexpr size_type _Least_allocation_size = _Small_string_capacity + 1 + 1;
2019-09-05 01:57:56 +03:00
// When doing _String_val operations by memcpy, we are touching:
// _String_val::_Bx::_Buf (type is array of _Elem)
// _String_val::_Bx::_Ptr (type is pointer)
// _String_val::_Mysize (type is size_type)
// _String_val::_Myres (type is size_type)
// N4950 [strings.general]/1 says _Elem must be trivial standard-layout, so memcpy is safe.
2019-09-05 01:57:56 +03:00
// We need to ask if pointer is safe to memcpy.
// size_type must be an unsigned integral type so memcpy is safe.
// We also need to disable memcpy if the user has supplied _Traits, since
// they can observe traits::assign and similar.
static constexpr bool _Can_memcpy_val = _Is_specialization_v<_Traits, char_traits> && is_trivial_v<pointer>;
// This offset skips over the _Container_base members, if any
static constexpr size_t _Memcpy_val_offset = _Size_after_ebco_v<_Container_base>;
static constexpr size_t _Memcpy_val_size = sizeof(_Scary_val) - _Memcpy_val_offset;
template <class _Iter>
// TRANSITION, /clr:pure is incompatible with templated static constexpr data members
// static constexpr bool _Is_elem_cptr =_Is_any_of_v<_Iter, const _Elem* const, _Elem* const, const _Elem*, _Elem*>;
2019-09-05 01:57:56 +03:00
using _Is_elem_cptr = bool_constant<_Is_any_of_v<_Iter, const _Elem* const, _Elem* const, const _Elem*, _Elem*>>;
#if _HAS_CXX17
template <class _StringViewIsh>
using _Is_string_view_ish =
enable_if_t<conjunction_v<is_convertible<const _StringViewIsh&, basic_string_view<_Elem, _Traits>>,
negation<is_convertible<const _StringViewIsh&, const _Elem*>>>,
int>;
2019-09-05 01:57:56 +03:00
#endif // _HAS_CXX17
#ifdef _INSERT_STRING_ANNOTATION
_CONSTEXPR20 void _Create_annotation() const noexcept {
// Annotates the valid range with shadow memory
auto& _My_data = _Mypair._Myval2;
_Apply_annotation(_My_data._Myptr(), _My_data._Myres, _My_data._Myres, _My_data._Mysize);
2019-09-05 01:57:56 +03:00
}
_CONSTEXPR20 void _Remove_annotation() const noexcept {
// Removes annotation of the range with shadow memory
auto& _My_data = _Mypair._Myval2;
_Apply_annotation(_My_data._Myptr(), _My_data._Myres, _My_data._Mysize, _My_data._Myres);
}
_CONSTEXPR20 void _Modify_annotation(const size_type _Old_size, const size_type _New_size) const noexcept {
if (_Old_size == _New_size) {
return;
}
auto& _My_data = _Mypair._Myval2;
_Apply_annotation(_My_data._Myptr(), _My_data._Myres, _Old_size, _New_size);
}
static _CONSTEXPR20 void _Apply_annotation(const value_type* const _First, const size_type _Capacity,
const size_type _Old_size, const size_type _New_size) noexcept {
#if _HAS_CXX20
if (_STD is_constant_evaluated()) {
return;
}
#endif // _HAS_CXX20
// Don't annotate small strings; only annotate on the heap.
if (_Capacity <= _Small_string_capacity || !_Asan_string_should_annotate) {
return;
}
// Note that `_Capacity`, `_Old_size`, and `_New_size` do not include the null terminator
const void* const _End = _First + _Capacity + 1;
const void* const _Old_last = _First + _Old_size + 1;
const void* const _New_last = _First + _New_size + 1;
constexpr bool _Large_string_always_asan_aligned =
(_Container_allocation_minimum_asan_alignment<basic_string>) >= _Asan_granularity;
// for the non-aligned buffer options, the buffer must always have size >= 9 bytes,
// so it will always end at least one shadow memory section.
_Asan_aligned_pointers _Aligned;
if constexpr (_Large_string_always_asan_aligned) {
_Aligned = {_First, _STD _Get_asan_aligned_after(_End)};
} else {
_Aligned = _STD _Get_asan_aligned_first_end(_First, _End);
}
const void* const _Old_fixed = _Aligned._Clamp_to_end(_Old_last);
const void* const _New_fixed = _Aligned._Clamp_to_end(_New_last);
// --- always aligned case ---
// old state:
// [_First, _Old_last) valid
// [_Old_last, asan_aligned_after(_End)) poison
// new state:
// [_First, _New_last) valid
// [_New_last, asan_aligned_after(_End)) poison
// --- sometimes non-aligned case ---
// old state:
// [_Aligned._First, _Old_fixed) valid
// [_Old_fixed, _Aligned._End) poison
// [_Aligned._End, _End) valid
// new state:
// [_Aligned._First, _New_fixed) valid
// [_New_fixed, _Aligned._End) poison
// [_Aligned._End, _End) valid
_CSTD __sanitizer_annotate_contiguous_container(_Aligned._First, _Aligned._End, _Old_fixed, _New_fixed);
}
#define _ASAN_STRING_REMOVE(_Str) (_Str)._Remove_annotation()
#define _ASAN_STRING_CREATE(_Str) (_Str)._Create_annotation()
#define _ASAN_STRING_MODIFY(_Str, _Old_size, _New_size) (_Str)._Modify_annotation(_Old_size, _New_size)
#else // ^^^ _INSERT_STRING_ANNOTATION / !_INSERT_STRING_ANNOTATION vvv
#define _ASAN_STRING_REMOVE(_Str)
#define _ASAN_STRING_CREATE(_Str)
#define _ASAN_STRING_MODIFY(_Str, _Old_size, _New_size)
#endif // ^^^ !_INSERT_STRING_ANNOTATION ^^^
public:
_CONSTEXPR20
basic_string() noexcept(is_nothrow_default_constructible_v<_Alty>) : _Mypair(_Zero_then_variadic_args_t{}) {
_Construct_empty();
2019-09-05 01:57:56 +03:00
}
_CONSTEXPR20 explicit basic_string(const _Alloc& _Al) noexcept : _Mypair(_One_then_variadic_args_t{}, _Al) {
_Construct_empty();
2019-09-05 01:57:56 +03:00
}
_CONSTEXPR20 basic_string(const basic_string& _Right)
: _Mypair(_One_then_variadic_args_t{}, _Alty_traits::select_on_container_copy_construction(_Right._Getal())) {
_Construct<_Construct_strategy::_From_string>(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize);
}
_CONSTEXPR20 basic_string(const basic_string& _Right, const _Alloc& _Al)
: _Mypair(_One_then_variadic_args_t{}, _Al) {
_Construct<_Construct_strategy::_From_string>(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize);
}
_CONSTEXPR20 basic_string(const basic_string& _Right, const size_type _Roff, const _Alloc& _Al = _Alloc())
: _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Right [_Roff, <end>)
_Right._Mypair._Myval2._Check_offset(_Roff);
_Construct<_Construct_strategy::_From_ptr>(
_Right._Mypair._Myval2._Myptr() + _Roff, _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, npos));
2019-09-05 01:57:56 +03:00
}
_CONSTEXPR20 basic_string(
2019-09-05 01:57:56 +03:00
const basic_string& _Right, const size_type _Roff, const size_type _Count, const _Alloc& _Al = _Alloc())
: _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Right [_Roff, _Roff + _Count)
_Right._Mypair._Myval2._Check_offset(_Roff);
_Construct<_Construct_strategy::_From_ptr>(
_Right._Mypair._Myval2._Myptr() + _Roff, _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count));
2019-09-05 01:57:56 +03:00
}
#if _HAS_CXX23
constexpr basic_string(basic_string&& _Right, const size_type _Roff, const _Alloc& _Al = _Alloc())
: _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Right [_Roff, <end>), potentially move
_Move_construct_from_substr(_Right, _Roff, npos);
}
constexpr basic_string(
basic_string&& _Right, const size_type _Roff, const size_type _Count, const _Alloc& _Al = _Alloc())
: _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Right [_Roff, _Roff + _Count), potentially move
_Move_construct_from_substr(_Right, _Roff, _Count);
}
#endif // _HAS_CXX23
_CONSTEXPR20 basic_string(_In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count)
: _Mypair(_Zero_then_variadic_args_t{}) {
_Construct<_Construct_strategy::_From_ptr>(_Ptr, _Count);
2019-09-05 01:57:56 +03:00
}
#if _HAS_CXX17
template <class _Alloc2 = _Alloc, enable_if_t<_Is_allocator<_Alloc2>::value, int> = 0>
#endif // _HAS_CXX17
_CONSTEXPR20 basic_string(
2019-09-05 01:57:56 +03:00
_In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count, const _Alloc& _Al)
: _Mypair(_One_then_variadic_args_t{}, _Al) {
_Construct<_Construct_strategy::_From_ptr>(_Ptr, _Count);
2019-09-05 01:57:56 +03:00
}
_CONSTEXPR20 basic_string(_In_z_ const _Elem* const _Ptr) : _Mypair(_Zero_then_variadic_args_t{}) {
_Construct<_Construct_strategy::_From_ptr>(_Ptr, _Convert_size<size_type>(_Traits::length(_Ptr)));
2019-09-05 01:57:56 +03:00
}
#if _HAS_CXX17
template <class _Alloc2 = _Alloc, enable_if_t<_Is_allocator<_Alloc2>::value, int> = 0>
#endif // _HAS_CXX17
_CONSTEXPR20 basic_string(_In_z_ const _Elem* const _Ptr, const _Alloc& _Al)
: _Mypair(_One_then_variadic_args_t{}, _Al) {
_Construct<_Construct_strategy::_From_ptr>(_Ptr, _Convert_size<size_type>(_Traits::length(_Ptr)));
2019-09-05 01:57:56 +03:00
}
#if _HAS_CXX23
basic_string(nullptr_t) = delete;
#endif // _HAS_CXX23
_CONSTEXPR20 basic_string(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch)
: _Mypair(_Zero_then_variadic_args_t{}) { // construct from _Count * _Ch
_Construct<_Construct_strategy::_From_char>(_Ch, _Count);
2019-09-05 01:57:56 +03:00
}
#if _HAS_CXX17
template <class _Alloc2 = _Alloc, enable_if_t<_Is_allocator<_Alloc2>::value, int> = 0>
#endif // _HAS_CXX17
_CONSTEXPR20 basic_string(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch, const _Alloc& _Al)
: _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Count * _Ch with allocator
_Construct<_Construct_strategy::_From_char>(_Ch, _Count);
2019-09-05 01:57:56 +03:00
}
template <class _Iter, enable_if_t<_Is_iterator_v<_Iter>, int> = 0>
_CONSTEXPR20 basic_string(_Iter _First, _Iter _Last, const _Alloc& _Al = _Alloc())
: _Mypair(_One_then_variadic_args_t{}, _Al) {
_STD _Adl_verify_range(_First, _Last);
auto _UFirst = _STD _Get_unwrapped(_First);
auto _ULast = _STD _Get_unwrapped(_Last);
if (_UFirst == _ULast) {
_Construct_empty();
} else {
if constexpr (_Is_elem_cptr<decltype(_UFirst)>::value) {
_Construct<_Construct_strategy::_From_ptr>(
_UFirst, _STD _Convert_size<size_type>(static_cast<size_t>(_ULast - _UFirst)));
} else if constexpr (_Is_cpp17_fwd_iter_v<decltype(_UFirst)>) {
const auto _Length = static_cast<size_t>(_STD distance(_UFirst, _ULast));
const auto _Count = _STD _Convert_size<size_type>(_Length);
_Construct_from_iter(_STD move(_UFirst), _STD move(_ULast), _Count);
} else {
_Construct_from_iter(_STD move(_UFirst), _STD move(_ULast));
}
}
}
private:
enum class _Allocation_policy { _At_least, _Exactly };
template <_Allocation_policy _Policy = _Allocation_policy::_At_least>
_NODISCARD static _CONSTEXPR20 pointer _Allocate_for_capacity(_Alty& _Al, size_type& _Capacity) {
_STL_INTERNAL_CHECK(_Capacity > _Small_string_capacity);
++_Capacity; // Take null terminator into consideration
pointer _Fancy_ptr = nullptr;
if constexpr (_Policy == _Allocation_policy::_At_least) {
_Fancy_ptr = _Allocate_at_least_helper(_Al, _Capacity);
} else {
_STL_INTERNAL_STATIC_ASSERT(_Policy == _Allocation_policy::_Exactly);
_Fancy_ptr = _Al.allocate(_Capacity);
}
#if _HAS_CXX20
// Start element lifetimes to avoid UB. This is a more general mechanism than _String_val::_Activate_SSO_buffer,
// but likely more impactful to throughput.
if (_STD is_constant_evaluated()) {
_Elem* const _Ptr = _Unfancy(_Fancy_ptr);
for (size_type _Idx = 0; _Idx < _Capacity; ++_Idx) {
_STD construct_at(_Ptr + _Idx);
}
}
#endif // _HAS_CXX20
--_Capacity;
return _Fancy_ptr;
}
static _CONSTEXPR20 void _Deallocate_for_capacity(
_Alty& _Al, const pointer _Old_ptr, const size_type _Capacity) noexcept {
_STL_INTERNAL_CHECK(_Capacity > _Small_string_capacity);
_Al.deallocate(_Old_ptr, _Capacity + 1); // +1 for null terminator
}
_CONSTEXPR20 void _Construct_empty() {
auto& _My_data = _Mypair._Myval2;
_My_data._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal()));
// initialize basic_string data members
_My_data._Mysize = 0;
_My_data._Myres = _Small_string_capacity;
_My_data._Activate_SSO_buffer();
// the _Traits::assign is last so the codegen doesn't think the char write can alias this
_Traits::assign(_My_data._Bx._Buf[0], _Elem());
}
enum class _Construct_strategy : uint8_t { _From_char, _From_ptr, _From_string };
template <_Construct_strategy _Strat, class _Char_or_ptr>
_CONSTEXPR20 void _Construct(const _Char_or_ptr _Arg, _CRT_GUARDOVERFLOW const size_type _Count) {
auto& _My_data = _Mypair._Myval2;
_STL_INTERNAL_CHECK(!_My_data._Large_mode_engaged());
if constexpr (_Strat == _Construct_strategy::_From_char) {
_STL_INTERNAL_STATIC_ASSERT(is_same_v<_Char_or_ptr, _Elem>);
} else {
_STL_INTERNAL_STATIC_ASSERT(_Is_elem_cptr<_Char_or_ptr>::value);
}
if (_Count > max_size()) {
_Xlen_string(); // result too long
}
auto& _Al = _Getal();
auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Al);
_Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data);
if (_Count <= _Small_string_capacity) {
_My_data._Mysize = _Count;
_My_data._Myres = _Small_string_capacity;
if constexpr (_Strat == _Construct_strategy::_From_char) {
_Traits::assign(_My_data._Bx._Buf, _Count, _Arg);
_Traits::assign(_My_data._Bx._Buf[_Count], _Elem());
} else if constexpr (_Strat == _Construct_strategy::_From_ptr) {
_Traits::copy(_My_data._Bx._Buf, _Arg, _Count);
_Traits::assign(_My_data._Bx._Buf[_Count], _Elem());
} else { // _Strat == _Construct_strategy::_From_string
#ifdef _INSERT_STRING_ANNOTATION
_Traits::copy(_My_data._Bx._Buf, _Arg, _Count + 1);
#else // ^^^ _INSERT_STRING_ANNOTATION / !_INSERT_STRING_ANNOTATION vvv
_Traits::copy(_My_data._Bx._Buf, _Arg, _BUF_SIZE);
#endif // ^^^ !_INSERT_STRING_ANNOTATION ^^^
}
_Proxy._Release();
return;
}
size_type _New_capacity = _Calculate_growth(_Count, _Small_string_capacity, max_size());
const pointer _New_ptr = _Allocate_for_capacity(_Al, _New_capacity); // throws
_Construct_in_place(_My_data._Bx._Ptr, _New_ptr);
_My_data._Mysize = _Count;
_My_data._Myres = _New_capacity;
if constexpr (_Strat == _Construct_strategy::_From_char) {
_Traits::assign(_Unfancy(_New_ptr), _Count, _Arg);
_Traits::assign(_Unfancy(_New_ptr)[_Count], _Elem());
} else if constexpr (_Strat == _Construct_strategy::_From_ptr) {
_Traits::copy(_Unfancy(_New_ptr), _Arg, _Count);
_Traits::assign(_Unfancy(_New_ptr)[_Count], _Elem());
} else { // _Strat == _Construct_strategy::_From_string
_Traits::copy(_Unfancy(_New_ptr), _Arg, _Count + 1);
}
_ASAN_STRING_CREATE(*this);
_Proxy._Release();
}
template <class _Iter, class _Sent, class _Size = nullptr_t>
_CONSTEXPR20 void _Construct_from_iter(_Iter _First, const _Sent _Last, _Size _Count = {}) {
// Pre: _Iter models input_iterator or meets the Cpp17InputIterator requirements.
// Pre: [_First, _Last) is a valid range.
// Pre: if _Iter models forward_iterator or meets the Cpp17ForwardIterator requirements,
// then is_same_v<_Size, size_type> holds.
// Pre: if is_same_v<_Size, size_type>, _Count is the length of [_First, _Last).
// Pre: *this is in small mode; the lifetime of the SSO elements has already begun.
auto& _My_data = _Mypair._Myval2;
auto& _Al = _Getal();
auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Al);
_Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data);
_My_data._Mysize = 0;
_My_data._Myres = _Small_string_capacity;
if constexpr (is_same_v<_Size, size_type>) {
if (_Count > max_size()) {
_Xlen_string(); // result too long
}
2019-09-05 01:57:56 +03:00
if (_Count > _Small_string_capacity) {
size_type _New_capacity = _Calculate_growth(_Count);
const pointer _New_ptr = _Allocate_for_capacity(_Al, _New_capacity); // throws
_Construct_in_place(_My_data._Bx._Ptr, _New_ptr);
_My_data._Myres = _New_capacity;
}
}
_Tidy_deallocate_guard<basic_string> _Guard{this};
constexpr bool _Can_construct_by_copy =
_Is_specialization_v<_Traits, char_traits> && _Is_EcharT<_Elem> && is_same_v<_Size, size_type>;
if constexpr (_Can_construct_by_copy) {
const auto _Data = _My_data._Myptr();
_STD _Copy_n_unchecked4(_STD move(_First), _Count, _Data);
_My_data._Mysize = _Count;
_Data[_Count] = _Elem();
} else {
for (; _First != _Last; ++_First) {
if constexpr (!is_same_v<_Size, size_type>) {
if (_My_data._Mysize == _My_data._Myres) { // Need to grow
if (_My_data._Mysize == max_size()) {
_Xlen_string(); // result too long
}
_Elem* const _Old_ptr = _My_data._Myptr();
size_type _New_capacity = _Calculate_growth(_My_data._Mysize + 1);
const pointer _New_ptr = _Allocate_for_capacity(_Al, _New_capacity); // throws
_Traits::copy(_Unfancy(_New_ptr), _Old_ptr, _My_data._Mysize);
if (_My_data._Large_mode_engaged()) { // Need to deallocate old storage
_Deallocate_for_capacity(_Al, _My_data._Bx._Ptr, _My_data._Myres);
_My_data._Bx._Ptr = _New_ptr;
} else {
_Construct_in_place(_My_data._Bx._Ptr, _New_ptr);
}
_My_data._Myres = _New_capacity;
}
}
_Elem* const _Ptr = _My_data._Myptr();
_Traits::assign(_Ptr[_My_data._Mysize], *_First);
++_My_data._Mysize;
}
2019-09-05 01:57:56 +03:00
_Elem* const _Ptr = _My_data._Myptr();
_Traits::assign(_Ptr[_My_data._Mysize], _Elem());
2019-09-05 01:57:56 +03:00
}
_ASAN_STRING_CREATE(*this);
_Guard._Target = nullptr;
_Proxy._Release();
2019-09-05 01:57:56 +03:00
}
public:
#if _HAS_CXX23
template <_Container_compatible_range<_Elem> _Rng>
constexpr basic_string(from_range_t, _Rng&& _Range, const _Alloc& _Al = _Alloc())
: _Mypair(_One_then_variadic_args_t{}, _Al) {
if constexpr (_RANGES sized_range<_Rng> || _RANGES forward_range<_Rng>) {
const auto _Length = _To_unsigned_like(_RANGES distance(_Range));
const auto _Count = _Convert_size<size_type>(_Length);
if constexpr (_Contiguous_range_of<_Rng, _Elem>) {
_Construct<_Construct_strategy::_From_ptr>(_RANGES data(_Range), _Count);
} else {
_Construct_from_iter(_RANGES _Ubegin(_Range), _RANGES _Uend(_Range), _Count);
}
} else {
_Construct_from_iter(_RANGES _Ubegin(_Range), _RANGES _Uend(_Range));
}
}
#endif // _HAS_CXX23
_CONSTEXPR20 basic_string(basic_string&& _Right) noexcept
: _Mypair(_One_then_variadic_args_t{}, _STD move(_Right._Getal())) {
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal()));
_Take_contents(_Right);
2019-09-05 01:57:56 +03:00
}
_CONSTEXPR20 basic_string(basic_string&& _Right, const _Alloc& _Al) noexcept(
2019-09-05 01:57:56 +03:00
_Alty_traits::is_always_equal::value) // strengthened
: _Mypair(_One_then_variadic_args_t{}, _Al) {
if constexpr (!_Alty_traits::is_always_equal::value) {
if (_Getal() != _Right._Getal()) {
_Construct<_Construct_strategy::_From_string>(
_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize);
return;
2019-09-05 01:57:56 +03:00
}
}
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal()));
_Take_contents(_Right);
2019-09-05 01:57:56 +03:00
}
_CONSTEXPR20 basic_string(_String_constructor_concat_tag, const basic_string& _Source_of_al,
const _Elem* const _Left_ptr, const size_type _Left_size, const _Elem* const _Right_ptr,
const size_type _Right_size)
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
: _Mypair(
_One_then_variadic_args_t{}, _Alty_traits::select_on_container_copy_construction(_Source_of_al._Getal())) {
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
_STL_INTERNAL_CHECK(_Left_size <= max_size());
_STL_INTERNAL_CHECK(_Right_size <= max_size());
_STL_INTERNAL_CHECK(_Right_size <= max_size() - _Left_size);
const auto _New_size = static_cast<size_type>(_Left_size + _Right_size);
size_type _New_capacity = _Small_string_capacity;
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
auto& _My_data = _Mypair._Myval2;
_Elem* _Ptr = _My_data._Bx._Buf;
auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal());
_Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data); // throws
if (_New_capacity < _New_size) {
_New_capacity = _Calculate_growth(_New_size, _Small_string_capacity, max_size());
const pointer _Fancyptr = _Allocate_for_capacity(_Getal(), _New_capacity); // throws
_Ptr = _Unfancy(_Fancyptr);
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
_Construct_in_place(_My_data._Bx._Ptr, _Fancyptr);
}
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
_My_data._Mysize = _New_size;
_My_data._Myres = _New_capacity;
_Traits::copy(_Ptr, _Left_ptr, _Left_size);
_Traits::copy(_Ptr + static_cast<ptrdiff_t>(_Left_size), _Right_ptr, _Right_size);
_Traits::assign(_Ptr[_New_size], _Elem());
_ASAN_STRING_CREATE(*this);
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
_Proxy._Release();
}
_CONSTEXPR20 basic_string(_String_constructor_concat_tag, basic_string& _Left, basic_string& _Right)
: _Mypair(_One_then_variadic_args_t{}, _Left._Getal()) {
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
auto& _My_data = _Mypair._Myval2;
auto& _Left_data = _Left._Mypair._Myval2;
auto& _Right_data = _Right._Mypair._Myval2;
_Left_data._Orphan_all();
_Right_data._Orphan_all();
const auto _Left_size = _Left_data._Mysize;
const auto _Right_size = _Right_data._Mysize;
const auto _Left_capacity = _Left_data._Myres;
const auto _Right_capacity = _Right_data._Myres;
// overflow is OK due to max_size() checks:
const auto _New_size = static_cast<size_type>(_Left_size + _Right_size);
const bool _Fits_in_left = _Right_size <= _Left_capacity - _Left_size;
if (_Fits_in_left && _Right_capacity <= _Left_capacity) {
// take _Left's buffer, max_size() is OK because _Fits_in_left
_My_data._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); // throws, hereafter nothrow in this block
_Take_contents(_Left);
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
const auto _Ptr = _My_data._Myptr();
_ASAN_STRING_MODIFY(*this, _Left_size, _New_size);
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
_Traits::copy(_Ptr + _Left_size, _Right_data._Myptr(), _Right_size + 1);
_My_data._Mysize = _New_size;
return;
}
const bool _Fits_in_right = _Left_size <= _Right_capacity - _Right_size;
if (_Allocators_equal(_Getal(), _Right._Getal()) && _Fits_in_right) {
// take _Right's buffer, max_size() is OK because _Fits_in_right
// At this point, we have tested:
// !(_Fits_in_left && _Right_capacity <= _Left_capacity) && _Fits_in_right
// therefore: (by De Morgan's Laws)
// (!_Fits_in_left || _Right_capacity > _Left_capacity) && _Fits_in_right
// therefore: (by the distributive property)
// (!_Fits_in_left && _Fits_in_right) // implying _Right has more capacity
// || (_Right_capacity > _Left_capacity && _Fits_in_right) // tests that _Right has more capacity
// therefore: _Right must have more than the minimum capacity, so it must be _Large_mode_engaged()
_STL_INTERNAL_CHECK(_Right_data._Large_mode_engaged());
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
_My_data._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); // throws, hereafter nothrow in this block
_Take_contents(_Right);
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
const auto _Ptr = _Unfancy(_My_data._Bx._Ptr);
_ASAN_STRING_MODIFY(*this, _Right_size, _New_size);
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
_Traits::move(_Ptr + _Left_size, _Ptr, _Right_size + 1);
_Traits::copy(_Ptr, _Left_data._Myptr(), _Left_size);
_My_data._Mysize = _New_size;
return;
}
// can't use either buffer, reallocate
const auto _Max = max_size();
if (_Max - _Left_size < _Right_size) { // check if max_size() is OK
_Xlen_string();
}
size_type _New_capacity = _Calculate_growth(_New_size, _Small_string_capacity, _Max);
auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal());
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
_Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data); // throws
const pointer _Fancyptr = _Allocate_for_capacity(_Getal(), _New_capacity); // throws
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
// nothrow hereafter
_Construct_in_place(_My_data._Bx._Ptr, _Fancyptr);
_My_data._Mysize = _New_size;
_My_data._Myres = _New_capacity;
const auto _Ptr = _Unfancy(_Fancyptr);
_Traits::copy(_Ptr, _Left_data._Myptr(), _Left_size);
_Traits::copy(_Ptr + _Left_size, _Right_data._Myptr(), _Right_size + 1);
_ASAN_STRING_CREATE(*this);
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
_Proxy._Release();
}
2019-09-05 01:57:56 +03:00
#if _HAS_CXX17
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_CONSTEXPR20 explicit basic_string(const _StringViewIsh& _Right, const _Alloc& _Al = _Alloc())
: _Mypair(_One_then_variadic_args_t{}, _Al) {
const basic_string_view<_Elem, _Traits> _As_view = _Right;
_Construct<_Construct_strategy::_From_ptr>(_As_view.data(), _Convert_size<size_type>(_As_view.size()));
2019-09-05 01:57:56 +03:00
}
template <class _Ty, enable_if_t<is_convertible_v<const _Ty&, basic_string_view<_Elem, _Traits>>, int> = 0>
_CONSTEXPR20 basic_string(
const _Ty& _Right, const size_type _Roff, const size_type _Count, const _Alloc& _Al = _Alloc())
: _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Right [_Roff, _Roff + _Count) using _Al
const basic_string_view<_Elem, _Traits> _As_view = _Right;
const auto _As_sub_view = _As_view.substr(_Roff, _Count);
_Construct<_Construct_strategy::_From_ptr>(_As_sub_view.data(), _Convert_size<size_type>(_As_sub_view.size()));
2019-09-05 01:57:56 +03:00
}
#endif // _HAS_CXX17
#if _HAS_CXX20
basic_string(_String_constructor_rvalue_allocator_tag, _Alloc&& _Al)
: _Mypair(_One_then_variadic_args_t{}, _STD move(_Al)) {
// Used exclusively by basic_stringbuf
_Construct_empty();
}
_NODISCARD bool _Move_assign_from_buffer(
_Elem* const _Right, const size_type _Size, const size_type _Actual_allocation_size) {
// Move assign from a buffer, used exclusively by basic_stringbuf; returns _Large_mode_engaged()
auto& _My_data = _Mypair._Myval2;
_STL_INTERNAL_CHECK(!_My_data._Large_mode_engaged() && _My_data._Mysize == 0);
_STL_INTERNAL_CHECK(_Size < _Actual_allocation_size); // So there is room for null terminator
_Traits::assign(_Right[_Size], _Elem());
const bool _Is_large = _Actual_allocation_size >= _Least_allocation_size;
if (_Is_large) {
_ASAN_STRING_REMOVE(*this);
_Construct_in_place(_My_data._Bx._Ptr, _Refancy<pointer>(_Right));
_My_data._Mysize = _Size;
_My_data._Myres = _Actual_allocation_size - 1;
_ASAN_STRING_CREATE(*this);
} else {
_Traits::copy(_My_data._Bx._Buf, _Right, _Actual_allocation_size);
_My_data._Mysize = _Size;
_My_data._Myres = _Small_string_capacity;
}
return _Is_large;
}
// No instance of this type can exist where an exception may be thrown.
struct _Released_buffer {
pointer _Ptr;
size_type _Size;
size_type _Actual_allocation_size;
};
_NODISCARD _Released_buffer _Release_to_buffer(_Alloc& _Al) {
// Release to a buffer, or allocate a new one if in small string mode; used exclusively by basic_stringbuf
_Released_buffer _Result;
auto& _My_data = _Mypair._Myval2;
_Result._Size = _My_data._Mysize;
_My_data._Orphan_all();
_ASAN_STRING_REMOVE(*this);
if (_My_data._Large_mode_engaged()) {
_Result._Ptr = _My_data._Bx._Ptr;
_Result._Actual_allocation_size = _My_data._Myres + 1;
_My_data._Bx._Switch_to_buf();
} else {
// use _Least_allocation_size to avoid small mode, if the buffer is assigned back
size_type _Allocated = _Least_allocation_size;
_Result._Ptr = _Allocate_at_least_helper(_Al, _Allocated);
_Traits::copy(_Unfancy(_Result._Ptr), _My_data._Bx._Buf, _BUF_SIZE);
_Result._Actual_allocation_size = _Allocated;
}
_My_data._Mysize = 0;
_My_data._Myres = _Small_string_capacity;
_Traits::assign(_My_data._Bx._Buf[0], _Elem());
return _Result;
}
#endif // _HAS_CXX20
_CONSTEXPR20 basic_string& operator=(basic_string&& _Right) noexcept(
_Choose_pocma_v<_Alty> != _Pocma_values::_No_propagate_allocators) {
if (this == _STD addressof(_Right)) {
return *this;
}
auto& _Al = _Getal();
auto& _Right_al = _Right._Getal();
constexpr auto _Pocma_val = _Choose_pocma_v<_Alty>;
if constexpr (_Pocma_val == _Pocma_values::_Propagate_allocators) {
if (_Al != _Right_al) {
// intentionally slams into noexcept on OOM, TRANSITION, VSO-466800
_Mypair._Myval2._Orphan_all();
_Mypair._Myval2._Reload_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Al), _GET_PROXY_ALLOCATOR(_Alty, _Right_al));
}
} else if constexpr (_Pocma_val == _Pocma_values::_No_propagate_allocators) {
if (_Al != _Right_al) {
assign(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize);
return *this;
}
2019-09-05 01:57:56 +03:00
}
_Tidy_deallocate();
_Pocma(_Al, _Right_al);
_Take_contents(_Right);
2019-09-05 01:57:56 +03:00
return *this;
}
_CONSTEXPR20 basic_string& assign(basic_string&& _Right) noexcept(noexcept(*this = _STD move(_Right))) {
2019-09-05 01:57:56 +03:00
*this = _STD move(_Right);
return *this;
}
private:
_CONSTEXPR20 void _Take_contents(basic_string& _Right) noexcept {
// assign by stealing _Right's buffer
2019-09-05 01:57:56 +03:00
// pre: this != &_Right
// pre: allocator propagation (POCMA) from _Right, if necessary, is complete
// pre: *this owns no memory, iterators orphaned
// (note: _Buf/_Ptr/_Mysize/_Myres may be garbage init)
auto& _My_data = _Mypair._Myval2;
auto& _Right_data = _Right._Mypair._Myval2;
#if !defined(_INSERT_STRING_ANNOTATION)
if constexpr (_Can_memcpy_val) {
#if _HAS_CXX20
if (!_STD is_constant_evaluated())
#endif // _HAS_CXX20
{
#if _ITERATOR_DEBUG_LEVEL != 0
if (_Right_data._Large_mode_engaged()) {
// take ownership of _Right's iterators along with its buffer
_Swap_proxy_and_iterators(_Right);
} else {
_Right_data._Orphan_all();
}
#endif // _ITERATOR_DEBUG_LEVEL != 0
const auto _My_data_mem =
reinterpret_cast<unsigned char*>(_STD addressof(_Mypair._Myval2)) + _Memcpy_val_offset;
const auto _Right_data_mem =
reinterpret_cast<const unsigned char*>(_STD addressof(_Right._Mypair._Myval2)) + _Memcpy_val_offset;
_CSTD memcpy(_My_data_mem, _Right_data_mem, _Memcpy_val_size);
_Right_data._Mysize = 0;
_Right_data._Myres = _Small_string_capacity;
_Right_data._Activate_SSO_buffer();
_Traits::assign(_Right_data._Bx._Buf[0], _Elem());
return;
}
}
#endif // !defined(_INSERT_STRING_ANNOTATION)
if (_Right_data._Large_mode_engaged()) { // steal buffer
2019-09-05 01:57:56 +03:00
_Swap_proxy_and_iterators(_Right);
_Construct_in_place(_My_data._Bx._Ptr, _Right_data._Bx._Ptr);
_Right_data._Bx._Switch_to_buf();
2019-09-05 01:57:56 +03:00
} else { // copy small string buffer
_Right_data._Orphan_all();
_My_data._Activate_SSO_buffer();
2019-09-05 01:57:56 +03:00
_Traits::copy(_My_data._Bx._Buf, _Right_data._Bx._Buf, _Right_data._Mysize + 1);
}
_My_data._Myres = _Right_data._Myres;
_My_data._Mysize = _Right_data._Mysize;
_Right_data._Mysize = 0;
_Right_data._Myres = _Small_string_capacity;
_Traits::assign(_Right_data._Bx._Buf[0], _Elem());
2019-09-05 01:57:56 +03:00
}
#if _HAS_CXX23
constexpr void _Move_construct_from_substr(basic_string& _Right, const size_type _Roff, const size_type _Size_max) {
auto& _Right_data = _Right._Mypair._Myval2;
_Right_data._Check_offset(_Roff);
const auto _Result_size = _Right_data._Clamp_suffix_size(_Roff, _Size_max);
const auto _Right_ptr = _Right_data._Myptr();
auto& _Al = _Getal();
if (_Allocators_equal(_Al, _Right._Getal()) && _Result_size > _Small_string_capacity) {
_Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Al));
if (_Roff != 0) {
_Traits::move(_Right_ptr, _Right_ptr + _Roff, _Result_size);
}
_Right._Eos(_Result_size);
_Take_contents(_Right);
} else {
_Construct<_Construct_strategy::_From_ptr>(_Right_ptr + _Roff, _Result_size);
}
}
#endif // _HAS_CXX23
2019-09-05 01:57:56 +03:00
public:
_CONSTEXPR20 basic_string(initializer_list<_Elem> _Ilist, const _Alloc& _Al = allocator_type())
: _Mypair(_One_then_variadic_args_t{}, _Al) {
_Construct<_Construct_strategy::_From_ptr>(_Ilist.begin(), _Convert_size<size_type>(_Ilist.size()));
2019-09-05 01:57:56 +03:00
}
_CONSTEXPR20 basic_string& operator=(initializer_list<_Elem> _Ilist) {
2019-09-05 01:57:56 +03:00
return assign(_Ilist.begin(), _Convert_size<size_type>(_Ilist.size()));
}
_CONSTEXPR20 basic_string& operator+=(initializer_list<_Elem> _Ilist) {
2019-09-05 01:57:56 +03:00
return append(_Ilist.begin(), _Convert_size<size_type>(_Ilist.size()));
}
_CONSTEXPR20 basic_string& assign(initializer_list<_Elem> _Ilist) {
2019-09-05 01:57:56 +03:00
return assign(_Ilist.begin(), _Convert_size<size_type>(_Ilist.size()));
}
_CONSTEXPR20 basic_string& append(initializer_list<_Elem> _Ilist) {
2019-09-05 01:57:56 +03:00
return append(_Ilist.begin(), _Convert_size<size_type>(_Ilist.size()));
}
_CONSTEXPR20 iterator insert(const const_iterator _Where, const initializer_list<_Elem> _Ilist) {
2019-09-05 01:57:56 +03:00
#if _ITERATOR_DEBUG_LEVEL != 0
_STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible");
#endif // _ITERATOR_DEBUG_LEVEL != 0
const auto _Off = static_cast<size_type>(_Unfancy(_Where._Ptr) - _Mypair._Myval2._Myptr());
insert(_Off, _Ilist.begin(), _Convert_size<size_type>(_Ilist.size()));
return begin() + static_cast<difference_type>(_Off);
}
_CONSTEXPR20 basic_string& replace(
2019-09-05 01:57:56 +03:00
const const_iterator _First, const const_iterator _Last, const initializer_list<_Elem> _Ilist) {
// replace with initializer_list
_STD _Adl_verify_range(_First, _Last);
2019-09-05 01:57:56 +03:00
#if _ITERATOR_DEBUG_LEVEL != 0
_STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible");
#endif // _ITERATOR_DEBUG_LEVEL != 0
const auto _Offset = static_cast<size_type>(_Unfancy(_First._Ptr) - _Mypair._Myval2._Myptr());
const auto _Length = static_cast<size_type>(_Last._Ptr - _First._Ptr);
return replace(_Offset, _Length, _Ilist.begin(), _Convert_size<size_type>(_Ilist.size()));
}
_CONSTEXPR20 ~basic_string() noexcept {
2019-09-05 01:57:56 +03:00
_Tidy_deallocate();
#if _ITERATOR_DEBUG_LEVEL != 0
auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal());
const auto _To_delete = _Mypair._Myval2._Myproxy;
_Mypair._Myval2._Myproxy = nullptr;
_Delete_plain_internal(_Alproxy, _To_delete);
#endif // _ITERATOR_DEBUG_LEVEL != 0
}
static constexpr auto npos{static_cast<size_type>(-1)};
_CONSTEXPR20 basic_string& operator=(const basic_string& _Right) {
if (this == _STD addressof(_Right)) {
return *this;
}
2019-09-05 01:57:56 +03:00
auto& _Al = _Getal();
const auto& _Right_al = _Right._Getal();
if constexpr (_Choose_pocca_v<_Alty>) {
if (_Al != _Right_al) {
auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Al);
auto&& _Right_alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Right_al);
_Container_proxy_ptr<_Alty> _New_proxy(_Right_alproxy, _Leave_proxy_unbound{}); // throws
const size_type _Right_size = _Right._Mypair._Myval2._Mysize;
const _Elem* const _Right_ptr = _Right._Mypair._Myval2._Myptr();
if (_Right_size > _Small_string_capacity) {
size_type _New_capacity = _Calculate_growth(_Right_size, _Small_string_capacity, _Right.max_size());
auto _Right_al_non_const = _Right_al;
const pointer _New_ptr = _Allocate_for_capacity(_Right_al_non_const, _New_capacity); // throws
_Traits::copy(_Unfancy(_New_ptr), _Right_ptr, _Right_size + 1);
_Tidy_deallocate();
_Construct_in_place(_Mypair._Myval2._Bx._Ptr, _New_ptr);
_Mypair._Myval2._Mysize = _Right_size;
_Mypair._Myval2._Myres = _New_capacity;
_ASAN_STRING_CREATE(*this);
} else {
_Tidy_deallocate();
_Traits::copy(_Mypair._Myval2._Bx._Buf, _Right_ptr, _Right_size + 1);
_Mypair._Myval2._Mysize = _Right_size;
_Mypair._Myval2._Myres = _Small_string_capacity;
}
2019-09-05 01:57:56 +03:00
_Pocca(_Al, _Right_al);
_New_proxy._Bind(_Alproxy, _STD addressof(_Mypair._Myval2));
return *this;
}
2019-09-05 01:57:56 +03:00
}
_Pocca(_Al, _Right_al);
assign(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize);
2019-09-05 01:57:56 +03:00
return *this;
}
#if _HAS_CXX17
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_CONSTEXPR20 basic_string& operator=(const _StringViewIsh& _Right) {
2019-09-05 01:57:56 +03:00
return assign(_Right);
}
#endif // _HAS_CXX17
_CONSTEXPR20 basic_string& operator=(_In_z_ const _Elem* const _Ptr) {
2019-09-05 01:57:56 +03:00
return assign(_Ptr);
}
#if _HAS_CXX23
basic_string& operator=(nullptr_t) = delete;
#endif // _HAS_CXX23
_CONSTEXPR20 basic_string& operator=(const _Elem _Ch) { // assign {_Ch, _Elem()}
_ASAN_STRING_MODIFY(*this, _Mypair._Myval2._Mysize, 1);
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Mysize = 1;
_Elem* const _Ptr = _Mypair._Myval2._Myptr();
_Traits::assign(_Ptr[0], _Ch);
_Traits::assign(_Ptr[1], _Elem());
return *this;
}
_CONSTEXPR20 basic_string& operator+=(const basic_string& _Right) {
2019-09-05 01:57:56 +03:00
return append(_Right);
}
#if _HAS_CXX17
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_CONSTEXPR20 basic_string& operator+=(const _StringViewIsh& _Right) {
2019-09-05 01:57:56 +03:00
return append(_Right);
}
#endif // _HAS_CXX17
_CONSTEXPR20 basic_string& operator+=(_In_z_ const _Elem* const _Ptr) { // append [_Ptr, <null>)
2019-09-05 01:57:56 +03:00
return append(_Ptr);
}
_CONSTEXPR20 basic_string& operator+=(_Elem _Ch) {
2019-09-05 01:57:56 +03:00
push_back(_Ch);
return *this;
}
_CONSTEXPR20 basic_string& append(const basic_string& _Right) {
2019-09-05 01:57:56 +03:00
return append(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize);
}
_CONSTEXPR20 basic_string& append(const basic_string& _Right, const size_type _Roff, size_type _Count = npos) {
2019-09-05 01:57:56 +03:00
// append _Right [_Roff, _Roff + _Count)
_Right._Mypair._Myval2._Check_offset(_Roff);
_Count = _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count);
return append(_Right._Mypair._Myval2._Myptr() + _Roff, _Count);
}
#if _HAS_CXX17
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_CONSTEXPR20 basic_string& append(const _StringViewIsh& _Right) {
2019-09-05 01:57:56 +03:00
const basic_string_view<_Elem, _Traits> _As_view = _Right;
return append(_As_view.data(), _Convert_size<size_type>(_As_view.size()));
}
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_CONSTEXPR20 basic_string& append(
const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) {
2019-09-05 01:57:56 +03:00
// append _Right [_Roff, _Roff + _Count)
basic_string_view<_Elem, _Traits> _As_view = _Right;
return append(_As_view.substr(_Roff, _Count));
}
#endif // _HAS_CXX17
_CONSTEXPR20 basic_string& append(
_In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) {
2019-09-05 01:57:56 +03:00
// append [_Ptr, _Ptr + _Count)
const size_type _Old_size = _Mypair._Myval2._Mysize;
if (_Count <= _Mypair._Myval2._Myres - _Old_size) {
_ASAN_STRING_MODIFY(*this, _Old_size, _Old_size + _Count);
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Mysize = _Old_size + _Count;
_Elem* const _Old_ptr = _Mypair._Myval2._Myptr();
_Traits::move(_Old_ptr + _Old_size, _Ptr, _Count);
_Traits::assign(_Old_ptr[_Old_size + _Count], _Elem());
return *this;
}
return _Reallocate_grow_by(
_Count,
[](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size, const _Elem* const _Ptr,
const size_type _Count) _STATIC_CALL_OPERATOR {
2019-09-05 01:57:56 +03:00
_Traits::copy(_New_ptr, _Old_ptr, _Old_size);
_Traits::copy(_New_ptr + _Old_size, _Ptr, _Count);
_Traits::assign(_New_ptr[_Old_size + _Count], _Elem());
},
_Ptr, _Count);
}
_CONSTEXPR20 basic_string& append(_In_z_ const _Elem* const _Ptr) { // append [_Ptr, <null>)
2019-09-05 01:57:56 +03:00
return append(_Ptr, _Convert_size<size_type>(_Traits::length(_Ptr)));
}
_CONSTEXPR20 basic_string& append(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) {
// append _Count * _Ch
2019-09-05 01:57:56 +03:00
const size_type _Old_size = _Mypair._Myval2._Mysize;
if (_Count <= _Mypair._Myval2._Myres - _Old_size) {
_ASAN_STRING_MODIFY(*this, _Old_size, _Old_size + _Count);
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Mysize = _Old_size + _Count;
_Elem* const _Old_ptr = _Mypair._Myval2._Myptr();
_Traits::assign(_Old_ptr + _Old_size, _Count, _Ch);
_Traits::assign(_Old_ptr[_Old_size + _Count], _Elem());
return *this;
}
return _Reallocate_grow_by(
_Count,
[](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size, const size_type _Count,
const _Elem _Ch) _STATIC_CALL_OPERATOR {
2019-09-05 01:57:56 +03:00
_Traits::copy(_New_ptr, _Old_ptr, _Old_size);
_Traits::assign(_New_ptr + _Old_size, _Count, _Ch);
_Traits::assign(_New_ptr[_Old_size + _Count], _Elem());
},
_Count, _Ch);
}
template <class _Iter, enable_if_t<_Is_iterator_v<_Iter>, int> = 0>
_CONSTEXPR20 basic_string& append(const _Iter _First, const _Iter _Last) {
// append [_First, _Last), input iterators
_STD _Adl_verify_range(_First, _Last);
const auto _UFirst = _STD _Get_unwrapped(_First);
const auto _ULast = _STD _Get_unwrapped(_Last);
2019-09-05 01:57:56 +03:00
if constexpr (_Is_elem_cptr<decltype(_UFirst)>::value) {
return append(_UFirst, _STD _Convert_size<size_type>(static_cast<size_t>(_ULast - _UFirst)));
2019-09-05 01:57:56 +03:00
} else {
const basic_string _Right(_UFirst, _ULast, get_allocator());
return append(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize);
}
}
#if _HAS_CXX23
template <_Container_compatible_range<_Elem> _Rng>
constexpr basic_string& append_range(_Rng&& _Range) {
if constexpr (_RANGES sized_range<_Rng> && _Contiguous_range_of<_Rng, _Elem>) {
const auto _Count = _Convert_size<size_type>(_To_unsigned_like(_RANGES size(_Range)));
return append(_RANGES data(_Range), _Count);
} else {
const basic_string _Right(from_range, _Range, get_allocator());
return append(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize);
}
}
#endif // _HAS_CXX23
_CONSTEXPR20 basic_string& assign(const basic_string& _Right) {
2019-09-05 01:57:56 +03:00
*this = _Right;
return *this;
}
_CONSTEXPR20 basic_string& assign(const basic_string& _Right, const size_type _Roff, size_type _Count = npos) {
2019-09-05 01:57:56 +03:00
// assign _Right [_Roff, _Roff + _Count)
_Right._Mypair._Myval2._Check_offset(_Roff);
_Count = _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count);
return assign(_Right._Mypair._Myval2._Myptr() + _Roff, _Count);
}
#if _HAS_CXX17
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_CONSTEXPR20 basic_string& assign(const _StringViewIsh& _Right) {
2019-09-05 01:57:56 +03:00
const basic_string_view<_Elem, _Traits> _As_view = _Right;
return assign(_As_view.data(), _Convert_size<size_type>(_As_view.size()));
}
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_CONSTEXPR20 basic_string& assign(
const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) {
2019-09-05 01:57:56 +03:00
// assign _Right [_Roff, _Roff + _Count)
basic_string_view<_Elem, _Traits> _As_view = _Right;
return assign(_As_view.substr(_Roff, _Count));
}
#endif // _HAS_CXX17
_CONSTEXPR20 basic_string& assign(
_In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) {
2019-09-05 01:57:56 +03:00
// assign [_Ptr, _Ptr + _Count)
if (_Count <= _Mypair._Myval2._Myres) {
_ASAN_STRING_REMOVE(*this);
2019-09-05 01:57:56 +03:00
_Elem* const _Old_ptr = _Mypair._Myval2._Myptr();
_Mypair._Myval2._Mysize = _Count;
_Traits::move(_Old_ptr, _Ptr, _Count);
_Traits::assign(_Old_ptr[_Count], _Elem());
_ASAN_STRING_CREATE(*this);
2019-09-05 01:57:56 +03:00
return *this;
}
return _Reallocate_for(
_Count,
[](_Elem* const _New_ptr, const size_type _Count, const _Elem* const _Ptr) _STATIC_CALL_OPERATOR {
2019-09-05 01:57:56 +03:00
_Traits::copy(_New_ptr, _Ptr, _Count);
_Traits::assign(_New_ptr[_Count], _Elem());
},
_Ptr);
}
_CONSTEXPR20 basic_string& assign(_In_z_ const _Elem* const _Ptr) {
2019-09-05 01:57:56 +03:00
return assign(_Ptr, _Convert_size<size_type>(_Traits::length(_Ptr)));
}
_CONSTEXPR20 basic_string& assign(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) {
// assign _Count * _Ch
2019-09-05 01:57:56 +03:00
if (_Count <= _Mypair._Myval2._Myres) {
_ASAN_STRING_MODIFY(*this, _Mypair._Myval2._Mysize, _Count);
2019-09-05 01:57:56 +03:00
_Elem* const _Old_ptr = _Mypair._Myval2._Myptr();
_Mypair._Myval2._Mysize = _Count;
_Traits::assign(_Old_ptr, _Count, _Ch);
_Traits::assign(_Old_ptr[_Count], _Elem());
return *this;
}
return _Reallocate_for(
_Count,
[](_Elem* const _New_ptr, const size_type _Count, const _Elem _Ch) _STATIC_CALL_OPERATOR {
2019-09-05 01:57:56 +03:00
_Traits::assign(_New_ptr, _Count, _Ch);
_Traits::assign(_New_ptr[_Count], _Elem());
},
_Ch);
}
template <class _Iter, enable_if_t<_Is_iterator_v<_Iter>, int> = 0>
_CONSTEXPR20 basic_string& assign(const _Iter _First, const _Iter _Last) {
_STD _Adl_verify_range(_First, _Last);
const auto _UFirst = _STD _Get_unwrapped(_First);
const auto _ULast = _STD _Get_unwrapped(_Last);
2019-09-05 01:57:56 +03:00
if constexpr (_Is_elem_cptr<decltype(_UFirst)>::value) {
return assign(_UFirst, _STD _Convert_size<size_type>(static_cast<size_t>(_ULast - _UFirst)));
2019-09-05 01:57:56 +03:00
} else {
basic_string _Right(_UFirst, _ULast, get_allocator());
if (_Mypair._Myval2._Myres < _Right._Mypair._Myval2._Myres) {
_Mypair._Myval2._Orphan_all();
_Swap_data(_Right);
2019-09-05 01:57:56 +03:00
return *this;
} else {
return assign(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize);
}
}
}
#if _HAS_CXX23
template <_Container_compatible_range<_Elem> _Rng>
constexpr basic_string& assign_range(_Rng&& _Range) {
if constexpr (_RANGES sized_range<_Rng> && _Contiguous_range_of<_Rng, _Elem>) {
const auto _Count = _Convert_size<size_type>(_To_unsigned_like(_RANGES size(_Range)));
return assign(_RANGES data(_Range), _Count);
} else {
basic_string _Right(from_range, _Range, get_allocator());
if (_Mypair._Myval2._Myres < _Right._Mypair._Myval2._Myres) {
_Mypair._Myval2._Orphan_all();
_Swap_data(_Right);
return *this;
} else {
return assign(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize);
}
}
}
#endif // _HAS_CXX23
_CONSTEXPR20 basic_string& insert(const size_type _Off, const basic_string& _Right) {
// insert _Right at _Off
2019-09-05 01:57:56 +03:00
return insert(_Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize);
}
_CONSTEXPR20 basic_string& insert(
2019-09-05 01:57:56 +03:00
const size_type _Off, const basic_string& _Right, const size_type _Roff, size_type _Count = npos) {
// insert _Right [_Roff, _Roff + _Count) at _Off
_Right._Mypair._Myval2._Check_offset(_Roff);
_Count = _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count);
return insert(_Off, _Right._Mypair._Myval2._Myptr() + _Roff, _Count);
}
#if _HAS_CXX17
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_CONSTEXPR20 basic_string& insert(const size_type _Off, const _StringViewIsh& _Right) {
// insert _Right at _Off
2019-09-05 01:57:56 +03:00
const basic_string_view<_Elem, _Traits> _As_view = _Right;
return insert(_Off, _As_view.data(), _Convert_size<size_type>(_As_view.size()));
}
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_CONSTEXPR20 basic_string& insert(
const size_type _Off, const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) {
// insert _Right [_Roff, _Roff + _Count) at _Off
2019-09-05 01:57:56 +03:00
basic_string_view<_Elem, _Traits> _As_view = _Right;
return insert(_Off, _As_view.substr(_Roff, _Count));
}
#endif // _HAS_CXX17
_CONSTEXPR20 basic_string& insert(
2019-09-05 01:57:56 +03:00
const size_type _Off, _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) {
// insert [_Ptr, _Ptr + _Count) at _Off
_Mypair._Myval2._Check_offset(_Off);
const size_type _Old_size = _Mypair._Myval2._Mysize;
// We can't check for overlapping ranges when constant evaluated since comparison of pointers into string
// literals is unspecified, so always reallocate and copy to the new buffer if constant evaluated.
#if _HAS_CXX20
const bool _Check_overlap = _Count <= _Mypair._Myval2._Myres - _Old_size && !_STD is_constant_evaluated();
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
const bool _Check_overlap = _Count <= _Mypair._Myval2._Myres - _Old_size;
#endif // ^^^ !_HAS_CXX20 ^^^
if (_Check_overlap) {
_ASAN_STRING_MODIFY(*this, _Old_size, _Old_size + _Count);
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Mysize = _Old_size + _Count;
_Elem* const _Old_ptr = _Mypair._Myval2._Myptr();
_Elem* const _Insert_at = _Old_ptr + _Off;
// the range [_Ptr, _Ptr + _Ptr_shifted_after) is left alone by moving the suffix out,
// while the range [_Ptr + _Ptr_shifted_after, _Ptr + _Count) shifts down by _Count
size_type _Ptr_shifted_after;
if (_Ptr + _Count <= _Insert_at || _Ptr > _Old_ptr + _Old_size) {
// inserted content is before the shifted region, or does not alias
2019-09-05 01:57:56 +03:00
_Ptr_shifted_after = _Count; // none of _Ptr's data shifts
} else if (_Insert_at <= _Ptr) { // all of [_Ptr, _Ptr + _Count) shifts
_Ptr_shifted_after = 0;
} else { // [_Ptr, _Ptr + _Count) contains _Insert_at, so only the part after _Insert_at shifts
_Ptr_shifted_after = static_cast<size_type>(_Insert_at - _Ptr);
}
_Traits::move(_Insert_at + _Count, _Insert_at, _Old_size - _Off + 1); // move suffix + null down
_Traits::copy(_Insert_at, _Ptr, _Ptr_shifted_after);
_Traits::copy(
_Insert_at + _Ptr_shifted_after, _Ptr + _Count + _Ptr_shifted_after, _Count - _Ptr_shifted_after);
return *this;
}
return _Reallocate_grow_by(
_Count,
[](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size, const size_type _Off,
const _Elem* const _Ptr, const size_type _Count) _STATIC_CALL_OPERATOR {
2019-09-05 01:57:56 +03:00
_Traits::copy(_New_ptr, _Old_ptr, _Off);
_Traits::copy(_New_ptr + _Off, _Ptr, _Count);
_Traits::copy(_New_ptr + _Off + _Count, _Old_ptr + _Off, _Old_size - _Off + 1);
},
_Off, _Ptr, _Count);
}
_CONSTEXPR20 basic_string& insert(const size_type _Off, _In_z_ const _Elem* const _Ptr) {
// insert [_Ptr, <null>) at _Off
2019-09-05 01:57:56 +03:00
return insert(_Off, _Ptr, _Convert_size<size_type>(_Traits::length(_Ptr)));
}
_CONSTEXPR20 basic_string& insert(
const size_type _Off, _CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) {
2019-09-05 01:57:56 +03:00
// insert _Count * _Ch at _Off
_Mypair._Myval2._Check_offset(_Off);
const size_type _Old_size = _Mypair._Myval2._Mysize;
if (_Count <= _Mypair._Myval2._Myres - _Old_size) {
_ASAN_STRING_MODIFY(*this, _Old_size, _Old_size + _Count);
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Mysize = _Old_size + _Count;
_Elem* const _Old_ptr = _Mypair._Myval2._Myptr();
_Elem* const _Insert_at = _Old_ptr + _Off;
_Traits::move(_Insert_at + _Count, _Insert_at, _Old_size - _Off + 1); // move suffix + null down
_Traits::assign(_Insert_at, _Count, _Ch); // fill hole
return *this;
}
return _Reallocate_grow_by(
_Count,
[](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size, const size_type _Off,
const size_type _Count, const _Elem _Ch) _STATIC_CALL_OPERATOR {
2019-09-05 01:57:56 +03:00
_Traits::copy(_New_ptr, _Old_ptr, _Off);
_Traits::assign(_New_ptr + _Off, _Count, _Ch);
_Traits::copy(_New_ptr + _Off + _Count, _Old_ptr + _Off, _Old_size - _Off + 1);
},
_Off, _Count, _Ch);
}
_CONSTEXPR20 iterator insert(const const_iterator _Where, const _Elem _Ch) { // insert _Ch at _Where
2019-09-05 01:57:56 +03:00
#if _ITERATOR_DEBUG_LEVEL != 0
_STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible");
#endif // _ITERATOR_DEBUG_LEVEL != 0
const auto _Off = static_cast<size_type>(_Unfancy(_Where._Ptr) - _Mypair._Myval2._Myptr());
insert(_Off, 1, _Ch);
return begin() + static_cast<difference_type>(_Off);
}
_CONSTEXPR20 iterator insert(
const const_iterator _Where, _CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) {
2019-09-05 01:57:56 +03:00
// insert _Count * _Elem at _Where
#if _ITERATOR_DEBUG_LEVEL != 0
_STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible");
#endif // _ITERATOR_DEBUG_LEVEL != 0
const auto _Off = static_cast<size_type>(_Unfancy(_Where._Ptr) - _Mypair._Myval2._Myptr());
insert(_Off, _Count, _Ch);
return begin() + static_cast<difference_type>(_Off);
}
template <class _Iter, enable_if_t<_Is_iterator_v<_Iter>, int> = 0>
_CONSTEXPR20 iterator insert(const const_iterator _Where, const _Iter _First, const _Iter _Last) {
2019-09-05 01:57:56 +03:00
// insert [_First, _Last) at _Where, input iterators
#if _ITERATOR_DEBUG_LEVEL != 0
_STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible");
#endif // _ITERATOR_DEBUG_LEVEL != 0
const auto _Off = static_cast<size_type>(_Unfancy(_Where._Ptr) - _Mypair._Myval2._Myptr());
_STD _Adl_verify_range(_First, _Last);
const auto _UFirst = _STD _Get_unwrapped(_First);
const auto _ULast = _STD _Get_unwrapped(_Last);
2019-09-05 01:57:56 +03:00
if constexpr (_Is_elem_cptr<decltype(_UFirst)>::value) {
insert(_Off, _UFirst, _STD _Convert_size<size_type>(static_cast<size_t>(_ULast - _UFirst)));
2019-09-05 01:57:56 +03:00
} else {
const basic_string _Right(_UFirst, _ULast, get_allocator());
insert(_Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize);
}
return begin() + static_cast<difference_type>(_Off);
}
#if _HAS_CXX23
template <_Container_compatible_range<_Elem> _Rng>
constexpr iterator insert_range(const const_iterator _Where, _Rng&& _Range) {
#if _ITERATOR_DEBUG_LEVEL != 0
_STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible");
#endif // _ITERATOR_DEBUG_LEVEL != 0
const auto _Off = static_cast<size_type>(_Unfancy(_Where._Ptr) - _Mypair._Myval2._Myptr());
if constexpr (_RANGES sized_range<_Rng> && _Contiguous_range_of<_Rng, _Elem>) {
const auto _Count = _Convert_size<size_type>(_To_unsigned_like(_RANGES size(_Range)));
insert(_Off, _RANGES data(_Range), _Count);
} else {
const basic_string _Right(from_range, _Range, get_allocator());
insert(_Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize);
}
return begin() + static_cast<difference_type>(_Off);
}
#endif // _HAS_CXX23
_CONSTEXPR20 basic_string& erase(const size_type _Off = 0) { // erase elements [_Off, ...)
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Check_offset(_Off);
_Eos(_Off);
return *this;
}
private:
_CONSTEXPR20 basic_string& _Erase_noexcept(const size_type _Off, size_type _Count) noexcept {
2019-09-05 01:57:56 +03:00
_Count = _Mypair._Myval2._Clamp_suffix_size(_Off, _Count);
const size_type _Old_size = _Mypair._Myval2._Mysize;
_Elem* const _My_ptr = _Mypair._Myval2._Myptr();
_Elem* const _Erase_at = _My_ptr + _Off;
const size_type _New_size = _Old_size - _Count;
_Traits::move(_Erase_at, _Erase_at + _Count, _New_size - _Off + 1); // move suffix + null up
_ASAN_STRING_MODIFY(*this, _Old_size, _New_size);
_Mypair._Myval2._Mysize = _New_size;
2019-09-05 01:57:56 +03:00
return *this;
}
public:
_CONSTEXPR20 basic_string& erase(const size_type _Off, const size_type _Count) {
// erase elements [_Off, _Off + _Count)
_Mypair._Myval2._Check_offset(_Off);
return _Erase_noexcept(_Off, _Count);
}
_CONSTEXPR20 iterator erase(const const_iterator _Where) noexcept /* strengthened */ {
2019-09-05 01:57:56 +03:00
#if _ITERATOR_DEBUG_LEVEL != 0
_STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible");
#endif // _ITERATOR_DEBUG_LEVEL != 0
const auto _Off = static_cast<size_type>(_Unfancy(_Where._Ptr) - _Mypair._Myval2._Myptr());
_Erase_noexcept(_Off, 1);
2019-09-05 01:57:56 +03:00
return begin() + static_cast<difference_type>(_Off);
}
_CONSTEXPR20 iterator erase(const const_iterator _First, const const_iterator _Last) noexcept
/* strengthened */ {
_STD _Adl_verify_range(_First, _Last);
2019-09-05 01:57:56 +03:00
#if _ITERATOR_DEBUG_LEVEL != 0
_STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible");
#endif // _ITERATOR_DEBUG_LEVEL != 0
const auto _Off = static_cast<size_type>(_Unfancy(_First._Ptr) - _Mypair._Myval2._Myptr());
_Erase_noexcept(_Off, static_cast<size_type>(_Last._Ptr - _First._Ptr));
2019-09-05 01:57:56 +03:00
return begin() + static_cast<difference_type>(_Off);
}
_CONSTEXPR20 void clear() noexcept { // erase all
2019-09-05 01:57:56 +03:00
_Eos(0);
}
_CONSTEXPR20 basic_string& replace(const size_type _Off, const size_type _Nx, const basic_string& _Right) {
// replace [_Off, _Off + _Nx) with _Right
return replace(_Off, _Nx, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize);
2019-09-05 01:57:56 +03:00
}
_CONSTEXPR20 basic_string& replace(const size_type _Off, size_type _Nx, const basic_string& _Right,
const size_type _Roff, size_type _Count = npos) {
// replace [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count)
2019-09-05 01:57:56 +03:00
_Right._Mypair._Myval2._Check_offset(_Roff);
_Count = _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count);
return replace(_Off, _Nx, _Right._Mypair._Myval2._Myptr() + _Roff, _Count);
2019-09-05 01:57:56 +03:00
}
#if _HAS_CXX17
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_CONSTEXPR20 basic_string& replace(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right) {
// replace [_Off, _Off + _Nx) with _Right
2019-09-05 01:57:56 +03:00
basic_string_view<_Elem, _Traits> _As_view = _Right;
return replace(_Off, _Nx, _As_view.data(), _Convert_size<size_type>(_As_view.size()));
2019-09-05 01:57:56 +03:00
}
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_CONSTEXPR20 basic_string& replace(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right,
const size_type _Roff, const size_type _Count = npos) {
// replace [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count)
2019-09-05 01:57:56 +03:00
basic_string_view<_Elem, _Traits> _As_view = _Right;
return replace(_Off, _Nx, _As_view.substr(_Roff, _Count));
2019-09-05 01:57:56 +03:00
}
#endif // _HAS_CXX17
_CONSTEXPR20 basic_string& replace(
const size_type _Off, size_type _Nx, _In_reads_(_Count) const _Elem* const _Ptr, const size_type _Count) {
// replace [_Off, _Off + _Nx) with [_Ptr, _Ptr + _Count)
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Check_offset(_Off);
_Nx = _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx);
if (_Nx == _Count) { // size doesn't change, so a single move does the trick
2019-09-05 01:57:56 +03:00
_Traits::move(_Mypair._Myval2._Myptr() + _Off, _Ptr, _Count);
return *this;
}
const size_type _Old_size = _Mypair._Myval2._Mysize;
const size_type _Suffix_size = _Old_size - _Nx - _Off + 1;
if (_Count < _Nx) { // suffix shifts backwards; we don't have to move anything out of the way
2019-09-05 01:57:56 +03:00
_Elem* const _Old_ptr = _Mypair._Myval2._Myptr();
_Elem* const _Insert_at = _Old_ptr + _Off;
_Traits::move(_Insert_at, _Ptr, _Count);
_Traits::move(_Insert_at + _Count, _Insert_at + _Nx, _Suffix_size);
const auto _New_size = _Old_size - (_Nx - _Count);
_ASAN_STRING_MODIFY(*this, _Old_size, _New_size);
_Mypair._Myval2._Mysize = _New_size;
2019-09-05 01:57:56 +03:00
return *this;
}
const size_type _Growth = static_cast<size_type>(_Count - _Nx);
2019-09-05 01:57:56 +03:00
// checking for overlapping ranges is technically UB (considering string literals), so just always reallocate
// and copy to the new buffer if constant evaluated
#if _HAS_CXX20
if (!_STD is_constant_evaluated())
#endif // _HAS_CXX20
{
if (_Growth <= _Mypair._Myval2._Myres - _Old_size) { // growth fits
_ASAN_STRING_MODIFY(*this, _Old_size, _Old_size + _Growth);
_Mypair._Myval2._Mysize = _Old_size + _Growth;
_Elem* const _Old_ptr = _Mypair._Myval2._Myptr();
_Elem* const _Insert_at = _Old_ptr + _Off;
_Elem* const _Suffix_at = _Insert_at + _Nx;
size_type _Ptr_shifted_after; // see rationale in insert
if (_Ptr + _Count <= _Insert_at || _Ptr > _Old_ptr + _Old_size) {
_Ptr_shifted_after = _Count;
} else if (_Suffix_at <= _Ptr) {
_Ptr_shifted_after = 0;
} else {
_Ptr_shifted_after = static_cast<size_type>(_Suffix_at - _Ptr);
}
2019-09-05 01:57:56 +03:00
_Traits::move(_Suffix_at + _Growth, _Suffix_at, _Suffix_size);
// next case must be move, in case _Ptr begins before _Insert_at and contains part of the hole;
// this case doesn't occur in insert because the new content must come from outside the removed
// content there (because in insert there is no removed content)
_Traits::move(_Insert_at, _Ptr, _Ptr_shifted_after);
// the next case can be copy, because it comes from the chunk moved out of the way in the
// first move, and the hole we're filling can't alias the chunk we moved out of the way
_Traits::copy(
_Insert_at + _Ptr_shifted_after, _Ptr + _Growth + _Ptr_shifted_after, _Count - _Ptr_shifted_after);
return *this;
}
2019-09-05 01:57:56 +03:00
}
return _Reallocate_grow_by(
_Growth,
[](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size, const size_type _Off,
const size_type _Nx, const _Elem* const _Ptr, const size_type _Count) _STATIC_CALL_OPERATOR {
2019-09-05 01:57:56 +03:00
_Traits::copy(_New_ptr, _Old_ptr, _Off);
_Traits::copy(_New_ptr + _Off, _Ptr, _Count);
_Traits::copy(_New_ptr + _Off + _Count, _Old_ptr + _Off + _Nx, _Old_size - _Nx - _Off + 1);
2019-09-05 01:57:56 +03:00
},
_Off, _Nx, _Ptr, _Count);
2019-09-05 01:57:56 +03:00
}
_CONSTEXPR20 basic_string& replace(const size_type _Off, const size_type _Nx, _In_z_ const _Elem* const _Ptr) {
// replace [_Off, _Off + _Nx) with [_Ptr, <null>)
return replace(_Off, _Nx, _Ptr, _Convert_size<size_type>(_Traits::length(_Ptr)));
2019-09-05 01:57:56 +03:00
}
_CONSTEXPR20 basic_string& replace(const size_type _Off, size_type _Nx, const size_type _Count, const _Elem _Ch) {
// replace [_Off, _Off + _Nx) with _Count * _Ch
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Check_offset(_Off);
_Nx = _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx);
if (_Count == _Nx) {
2019-09-05 01:57:56 +03:00
_Traits::assign(_Mypair._Myval2._Myptr() + _Off, _Count, _Ch);
return *this;
}
const size_type _Old_size = _Mypair._Myval2._Mysize;
if (_Count < _Nx || _Count - _Nx <= _Mypair._Myval2._Myres - _Old_size) {
// either we are shrinking, or the growth fits
// may temporarily overflow; OK because size_type must be unsigned
const auto _New_size = _Old_size + _Count - _Nx;
_ASAN_STRING_REMOVE(*this);
_Mypair._Myval2._Mysize = _New_size;
2019-09-05 01:57:56 +03:00
_Elem* const _Old_ptr = _Mypair._Myval2._Myptr();
_Elem* const _Insert_at = _Old_ptr + _Off;
_Traits::move(_Insert_at + _Count, _Insert_at + _Nx, _Old_size - _Nx - _Off + 1);
2019-09-05 01:57:56 +03:00
_Traits::assign(_Insert_at, _Count, _Ch);
_ASAN_STRING_CREATE(*this);
2019-09-05 01:57:56 +03:00
return *this;
}
return _Reallocate_grow_by(
_Count - _Nx,
2019-09-05 01:57:56 +03:00
[](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size, const size_type _Off,
const size_type _Nx, const size_type _Count, const _Elem _Ch) _STATIC_CALL_OPERATOR {
2019-09-05 01:57:56 +03:00
_Traits::copy(_New_ptr, _Old_ptr, _Off);
_Traits::assign(_New_ptr + _Off, _Count, _Ch);
_Traits::copy(_New_ptr + _Off + _Count, _Old_ptr + _Off + _Nx, _Old_size - _Nx - _Off + 1);
2019-09-05 01:57:56 +03:00
},
_Off, _Nx, _Count, _Ch);
2019-09-05 01:57:56 +03:00
}
_CONSTEXPR20 basic_string& replace(
const const_iterator _First, const const_iterator _Last, const basic_string& _Right) {
2019-09-05 01:57:56 +03:00
// replace [_First, _Last) with _Right
_STD _Adl_verify_range(_First, _Last);
2019-09-05 01:57:56 +03:00
#if _ITERATOR_DEBUG_LEVEL != 0
_STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible");
#endif // _ITERATOR_DEBUG_LEVEL != 0
return replace(static_cast<size_type>(_Unfancy(_First._Ptr) - _Mypair._Myval2._Myptr()),
static_cast<size_type>(_Last._Ptr - _First._Ptr), _Right);
}
#if _HAS_CXX17
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_CONSTEXPR20 basic_string& replace(
const const_iterator _First, const const_iterator _Last, const _StringViewIsh& _Right) {
2019-09-05 01:57:56 +03:00
// replace [_First, _Last) with _Right
_STD _Adl_verify_range(_First, _Last);
2019-09-05 01:57:56 +03:00
#if _ITERATOR_DEBUG_LEVEL != 0
_STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible");
#endif // _ITERATOR_DEBUG_LEVEL != 0
return replace(static_cast<size_type>(_Unfancy(_First._Ptr) - _Mypair._Myval2._Myptr()),
static_cast<size_type>(_Last._Ptr - _First._Ptr), _Right);
}
#endif // _HAS_CXX17
_CONSTEXPR20 basic_string& replace(const const_iterator _First, const const_iterator _Last,
2019-09-05 01:57:56 +03:00
_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Count) {
// replace [_First, _Last) with [_Ptr, _Ptr + _Count)
_STD _Adl_verify_range(_First, _Last);
2019-09-05 01:57:56 +03:00
#if _ITERATOR_DEBUG_LEVEL != 0
_STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible");
#endif // _ITERATOR_DEBUG_LEVEL != 0
return replace(static_cast<size_type>(_Unfancy(_First._Ptr) - _Mypair._Myval2._Myptr()),
static_cast<size_type>(_Last._Ptr - _First._Ptr), _Ptr, _Count);
}
_CONSTEXPR20 basic_string& replace(
const const_iterator _First, const const_iterator _Last, _In_z_ const _Elem* const _Ptr) {
2019-09-05 01:57:56 +03:00
// replace [_First, _Last) with [_Ptr, <null>)
_STD _Adl_verify_range(_First, _Last);
2019-09-05 01:57:56 +03:00
#if _ITERATOR_DEBUG_LEVEL != 0
_STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible");
#endif // _ITERATOR_DEBUG_LEVEL != 0
return replace(static_cast<size_type>(_Unfancy(_First._Ptr) - _Mypair._Myval2._Myptr()),
static_cast<size_type>(_Last._Ptr - _First._Ptr), _Ptr);
}
_CONSTEXPR20 basic_string& replace(
2019-09-05 01:57:56 +03:00
const const_iterator _First, const const_iterator _Last, const size_type _Count, const _Elem _Ch) {
// replace [_First, _Last) with _Count * _Ch
_STD _Adl_verify_range(_First, _Last);
2019-09-05 01:57:56 +03:00
#if _ITERATOR_DEBUG_LEVEL != 0
_STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible");
#endif // _ITERATOR_DEBUG_LEVEL != 0
return replace(static_cast<size_type>(_Unfancy(_First._Ptr) - _Mypair._Myval2._Myptr()),
static_cast<size_type>(_Last._Ptr - _First._Ptr), _Count, _Ch);
}
template <class _Iter, enable_if_t<_Is_iterator_v<_Iter>, int> = 0>
_CONSTEXPR20 basic_string& replace(
2019-09-05 01:57:56 +03:00
const const_iterator _First, const const_iterator _Last, const _Iter _First2, const _Iter _Last2) {
// replace [_First, _Last) with [_First2, _Last2), input iterators
_STD _Adl_verify_range(_First, _Last);
2019-09-05 01:57:56 +03:00
#if _ITERATOR_DEBUG_LEVEL != 0
_STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible");
#endif // _ITERATOR_DEBUG_LEVEL != 0
const auto _Off = static_cast<size_type>(_STD _Unfancy(_First._Ptr) - _Mypair._Myval2._Myptr());
2019-09-05 01:57:56 +03:00
const auto _Length = static_cast<size_type>(_Last._Ptr - _First._Ptr);
_STD _Adl_verify_range(_First2, _Last2);
const auto _UFirst2 = _STD _Get_unwrapped(_First2);
const auto _ULast2 = _STD _Get_unwrapped(_Last2);
2019-09-05 01:57:56 +03:00
if constexpr (_Is_elem_cptr<decltype(_UFirst2)>::value) {
return replace(
_Off, _Length, _UFirst2, _STD _Convert_size<size_type>(static_cast<size_t>(_ULast2 - _UFirst2)));
2019-09-05 01:57:56 +03:00
} else {
const basic_string _Right(_UFirst2, _ULast2, get_allocator());
return replace(_Off, _Length, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize);
}
}
#if _HAS_CXX23
template <_Container_compatible_range<_Elem> _Rng>
constexpr basic_string& replace_with_range(const const_iterator _First, const const_iterator _Last, _Rng&& _Range) {
_STD _Adl_verify_range(_First, _Last);
#if _ITERATOR_DEBUG_LEVEL != 0
_STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible");
#endif // _ITERATOR_DEBUG_LEVEL != 0
const auto _Off = static_cast<size_type>(_Unfancy(_First._Ptr) - _Mypair._Myval2._Myptr());
const auto _Length = static_cast<size_type>(_Last._Ptr - _First._Ptr);
if constexpr (_RANGES sized_range<_Rng> && _Contiguous_range_of<_Rng, _Elem>) {
const auto _Count = _Convert_size<size_type>(_To_unsigned_like(_RANGES size(_Range)));
return replace(_Off, _Length, _RANGES data(_Range), _Count);
} else {
const basic_string _Right(from_range, _Range, get_allocator());
return replace(_Off, _Length, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize);
}
}
#endif // _HAS_CXX23
_NODISCARD _CONSTEXPR20 iterator begin() noexcept {
2019-09-05 01:57:56 +03:00
return iterator(_Refancy<pointer>(_Mypair._Myval2._Myptr()), _STD addressof(_Mypair._Myval2));
}
_NODISCARD _CONSTEXPR20 const_iterator begin() const noexcept {
2019-09-05 01:57:56 +03:00
return const_iterator(_Refancy<const_pointer>(_Mypair._Myval2._Myptr()), _STD addressof(_Mypair._Myval2));
}
_NODISCARD _CONSTEXPR20 iterator end() noexcept {
2019-09-05 01:57:56 +03:00
return iterator(
_Refancy<pointer>(_Mypair._Myval2._Myptr()) + static_cast<difference_type>(_Mypair._Myval2._Mysize),
_STD addressof(_Mypair._Myval2));
}
_NODISCARD _CONSTEXPR20 const_iterator end() const noexcept {
2019-09-05 01:57:56 +03:00
return const_iterator(
_Refancy<const_pointer>(_Mypair._Myval2._Myptr()) + static_cast<difference_type>(_Mypair._Myval2._Mysize),
_STD addressof(_Mypair._Myval2));
}
_NODISCARD _CONSTEXPR20 _Elem* _Unchecked_begin() noexcept {
2019-09-05 01:57:56 +03:00
return _Mypair._Myval2._Myptr();
}
_NODISCARD _CONSTEXPR20 const _Elem* _Unchecked_begin() const noexcept {
2019-09-05 01:57:56 +03:00
return _Mypair._Myval2._Myptr();
}
_NODISCARD _CONSTEXPR20 _Elem* _Unchecked_end() noexcept {
2019-09-05 01:57:56 +03:00
return _Mypair._Myval2._Myptr() + _Mypair._Myval2._Mysize;
}
_NODISCARD _CONSTEXPR20 const _Elem* _Unchecked_end() const noexcept {
2019-09-05 01:57:56 +03:00
return _Mypair._Myval2._Myptr() + _Mypair._Myval2._Mysize;
}
_NODISCARD _CONSTEXPR20 reverse_iterator rbegin() noexcept {
2019-09-05 01:57:56 +03:00
return reverse_iterator(end());
}
_NODISCARD _CONSTEXPR20 const_reverse_iterator rbegin() const noexcept {
2019-09-05 01:57:56 +03:00
return const_reverse_iterator(end());
}
_NODISCARD _CONSTEXPR20 reverse_iterator rend() noexcept {
2019-09-05 01:57:56 +03:00
return reverse_iterator(begin());
}
_NODISCARD _CONSTEXPR20 const_reverse_iterator rend() const noexcept {
2019-09-05 01:57:56 +03:00
return const_reverse_iterator(begin());
}
_NODISCARD _CONSTEXPR20 const_iterator cbegin() const noexcept {
2019-09-05 01:57:56 +03:00
return begin();
}
_NODISCARD _CONSTEXPR20 const_iterator cend() const noexcept {
2019-09-05 01:57:56 +03:00
return end();
}
_NODISCARD _CONSTEXPR20 const_reverse_iterator crbegin() const noexcept {
2019-09-05 01:57:56 +03:00
return rbegin();
}
_NODISCARD _CONSTEXPR20 const_reverse_iterator crend() const noexcept {
2019-09-05 01:57:56 +03:00
return rend();
}
_CONSTEXPR20 void shrink_to_fit() { // reduce capacity
2019-09-05 01:57:56 +03:00
auto& _My_data = _Mypair._Myval2;
if (!_My_data._Large_mode_engaged()) { // can't shrink from small mode
return;
}
if (_My_data._Mysize <= _Small_string_capacity) {
_Become_small();
return;
2019-09-05 01:57:56 +03:00
}
size_type _Target_capacity = (_STD min)(_My_data._Mysize | _Alloc_mask, max_size());
2019-09-05 01:57:56 +03:00
if (_Target_capacity < _My_data._Myres) { // worth shrinking, do it
auto& _Al = _Getal();
const pointer _New_ptr =
_Allocate_for_capacity<_Allocation_policy::_Exactly>(_Al, _Target_capacity); // throws
_ASAN_STRING_REMOVE(*this);
2019-09-05 01:57:56 +03:00
_My_data._Orphan_all();
_Traits::copy(_Unfancy(_New_ptr), _Unfancy(_My_data._Bx._Ptr), _My_data._Mysize + 1);
_Deallocate_for_capacity(_Al, _My_data._Bx._Ptr, _My_data._Myres);
2019-09-05 01:57:56 +03:00
_My_data._Bx._Ptr = _New_ptr;
_My_data._Myres = _Target_capacity;
_ASAN_STRING_CREATE(*this);
2019-09-05 01:57:56 +03:00
}
}
_NODISCARD _CONSTEXPR20 reference at(const size_type _Off) {
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Check_offset_exclusive(_Off);
return _Mypair._Myval2._Myptr()[_Off];
}
_NODISCARD _CONSTEXPR20 const_reference at(const size_type _Off) const {
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Check_offset_exclusive(_Off);
return _Mypair._Myval2._Myptr()[_Off];
}
_NODISCARD _CONSTEXPR20 reference operator[](const size_type _Off) noexcept /* strengthened */ {
2019-09-05 01:57:56 +03:00
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Off <= _Mypair._Myval2._Mysize, "string subscript out of range");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _Mypair._Myval2._Myptr()[_Off];
}
_NODISCARD _CONSTEXPR20 const_reference operator[](const size_type _Off) const noexcept
/* strengthened */ {
2019-09-05 01:57:56 +03:00
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Off <= _Mypair._Myval2._Mysize, "string subscript out of range");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _Mypair._Myval2._Myptr()[_Off];
}
#if _HAS_CXX17
/* implicit */ _CONSTEXPR20 operator basic_string_view<_Elem, _Traits>() const noexcept {
2019-09-05 01:57:56 +03:00
// return a string_view around *this's character-type sequence
return basic_string_view<_Elem, _Traits>{_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize};
2019-09-05 01:57:56 +03:00
}
#endif // _HAS_CXX17
_CONSTEXPR20 void push_back(const _Elem _Ch) { // insert element at end
2019-09-05 01:57:56 +03:00
const size_type _Old_size = _Mypair._Myval2._Mysize;
if (_Old_size < _Mypair._Myval2._Myres) {
_ASAN_STRING_MODIFY(*this, _Old_size, _Old_size + 1);
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Mysize = _Old_size + 1;
_Elem* const _Ptr = _Mypair._Myval2._Myptr();
_Traits::assign(_Ptr[_Old_size], _Ch);
_Traits::assign(_Ptr[_Old_size + 1], _Elem());
return;
}
_Reallocate_grow_by(
1,
[](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size, const _Elem _Ch)
_STATIC_CALL_OPERATOR {
_Traits::copy(_New_ptr, _Old_ptr, _Old_size);
_Traits::assign(_New_ptr[_Old_size], _Ch);
_Traits::assign(_New_ptr[_Old_size + 1], _Elem());
},
2019-09-05 01:57:56 +03:00
_Ch);
}
_CONSTEXPR20 void pop_back() noexcept /* strengthened */ {
2019-09-05 01:57:56 +03:00
const size_type _Old_size = _Mypair._Myval2._Mysize;
#if _ITERATOR_DEBUG_LEVEL >= 1
_STL_VERIFY(_Old_size != 0, "invalid to pop_back empty string");
#endif // _ITERATOR_DEBUG_LEVEL >= 1
_Eos(_Old_size - 1);
}
_NODISCARD _CONSTEXPR20 reference front() noexcept /* strengthened */ {
2019-09-05 01:57:56 +03:00
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Mypair._Myval2._Mysize != 0, "front() called on empty string");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _Mypair._Myval2._Myptr()[0];
}
_NODISCARD _CONSTEXPR20 const_reference front() const noexcept /* strengthened */ {
2019-09-05 01:57:56 +03:00
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Mypair._Myval2._Mysize != 0, "front() called on empty string");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _Mypair._Myval2._Myptr()[0];
}
_NODISCARD _CONSTEXPR20 reference back() noexcept /* strengthened */ {
2019-09-05 01:57:56 +03:00
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Mypair._Myval2._Mysize != 0, "back() called on empty string");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _Mypair._Myval2._Myptr()[_Mypair._Myval2._Mysize - 1];
}
_NODISCARD _CONSTEXPR20 const_reference back() const noexcept /* strengthened */ {
2019-09-05 01:57:56 +03:00
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Mypair._Myval2._Mysize != 0, "back() called on empty string");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _Mypair._Myval2._Myptr()[_Mypair._Myval2._Mysize - 1];
}
_NODISCARD _CONSTEXPR20 _Ret_z_ const _Elem* c_str() const noexcept {
2019-09-05 01:57:56 +03:00
return _Mypair._Myval2._Myptr();
}
_NODISCARD _CONSTEXPR20 _Ret_z_ const _Elem* data() const noexcept {
2019-09-05 01:57:56 +03:00
return _Mypair._Myval2._Myptr();
}
#if _HAS_CXX17
_NODISCARD _CONSTEXPR20 _Ret_z_ _Elem* data() noexcept {
2019-09-05 01:57:56 +03:00
return _Mypair._Myval2._Myptr();
}
#endif // _HAS_CXX17
_NODISCARD _CONSTEXPR20 size_type length() const noexcept {
2019-09-05 01:57:56 +03:00
return _Mypair._Myval2._Mysize;
}
_NODISCARD _CONSTEXPR20 size_type size() const noexcept {
2019-09-05 01:57:56 +03:00
return _Mypair._Myval2._Mysize;
}
_NODISCARD _CONSTEXPR20 size_type max_size() const noexcept {
2019-09-05 01:57:56 +03:00
const size_type _Alloc_max = _Alty_traits::max_size(_Getal());
const size_type _Storage_max = // can always store small string
(_STD max)(_Alloc_max, static_cast<size_type>(_BUF_SIZE));
return (_STD min)(static_cast<size_type>(_STD _Max_limit<difference_type>()),
2019-09-05 01:57:56 +03:00
_Storage_max - 1 // -1 is for null terminator and/or npos
);
}
_CONSTEXPR20 void resize(_CRT_GUARDOVERFLOW const size_type _New_size, const _Elem _Ch = _Elem()) {
2019-09-05 01:57:56 +03:00
// determine new length, padding with _Ch elements as needed
const size_type _Old_size = size();
if (_New_size <= _Old_size) {
_Eos(_New_size);
2019-09-05 01:57:56 +03:00
} else {
append(_New_size - _Old_size, _Ch);
2019-09-05 01:57:56 +03:00
}
}
template <class _Operation>
constexpr void
#if _HAS_CXX23
resize_and_overwrite
#else // ^^^ _HAS_CXX23 / !_HAS_CXX23 vvv
_Resize_and_overwrite
#endif // ^^^ !_HAS_CXX23 ^^^
(_CRT_GUARDOVERFLOW const size_type _New_size, _Operation _Op) {
if (_Mypair._Myval2._Myres < _New_size) {
_Reallocate_grow_by(_New_size - _Mypair._Myval2._Mysize,
[](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size)
_STATIC_CALL_OPERATOR { _Traits::copy(_New_ptr, _Old_ptr, _Old_size + 1); });
} else {
_ASAN_STRING_MODIFY(*this, _Mypair._Myval2._Mysize, _New_size);
_Mypair._Myval2._Mysize = _New_size;
}
auto _Arg_ptr = _Mypair._Myval2._Myptr();
auto _Arg_size = _New_size;
const auto _Result_size = _STD move(_Op)(_Arg_ptr, _Arg_size);
const auto _Result_as_size_type = static_cast<size_type>(_Result_size);
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Result_size >= 0, "the returned size can't be smaller than 0");
_STL_VERIFY(_Result_as_size_type <= _New_size, "the returned size can't be greater than the passed size");
#endif // _CONTAINER_DEBUG_LEVEL > 0
_Eos(_Result_as_size_type);
}
#if _HAS_CXX23
template <class _Operation>
constexpr void _Resize_and_overwrite(_CRT_GUARDOVERFLOW const size_type _New_size, _Operation _Op) {
resize_and_overwrite(_New_size, _Op);
}
#endif // _HAS_CXX23
_NODISCARD _CONSTEXPR20 size_type capacity() const noexcept {
2019-09-05 01:57:56 +03:00
return _Mypair._Myval2._Myres;
}
#if _HAS_CXX20
constexpr void reserve(_CRT_GUARDOVERFLOW const size_type _Newcap) {
// determine new minimum length of allocated storage
if (_Mypair._Myval2._Myres >= _Newcap) { // requested capacity is not larger than current capacity, ignore
return; // nothing to do
}
const size_type _Old_size = _Mypair._Myval2._Mysize;
_Reallocate_grow_by(_Newcap - _Old_size,
[](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size)
_STATIC_CALL_OPERATOR { _Traits::copy(_New_ptr, _Old_ptr, _Old_size + 1); });
_Mypair._Myval2._Mysize = _Old_size;
}
_CXX20_DEPRECATE_STRING_RESERVE_WITHOUT_ARGUMENT void reserve() {
if (_Mypair._Myval2._Mysize == 0 && _Mypair._Myval2._Large_mode_engaged()) {
_Become_small();
}
}
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
2019-09-05 01:57:56 +03:00
void reserve(_CRT_GUARDOVERFLOW const size_type _Newcap = 0) { // determine new minimum length of allocated storage
if (_Mypair._Myval2._Mysize > _Newcap) { // requested capacity is not large enough for current size, ignore
return; // nothing to do
}
if (_Mypair._Myval2._Myres == _Newcap) { // we're already at the requested capacity
return; // nothing to do
}
if (_Mypair._Myval2._Myres < _Newcap) { // reallocate to grow
const size_type _Old_size = _Mypair._Myval2._Mysize;
_Reallocate_grow_by(_Newcap - _Old_size,
[](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size)
_STATIC_CALL_OPERATOR { _Traits::copy(_New_ptr, _Old_ptr, _Old_size + 1); });
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Mysize = _Old_size;
return;
}
if (_Newcap <= _Small_string_capacity && _Mypair._Myval2._Large_mode_engaged()) {
2019-09-05 01:57:56 +03:00
// deallocate everything; switch back to "small" mode
_Become_small();
return;
}
// ignore requests to reserve to [_Small_string_capacity + 1, _Myres)
2019-09-05 01:57:56 +03:00
}
#endif // ^^^ !_HAS_CXX20 ^^^
2019-09-05 01:57:56 +03:00
_NODISCARD_EMPTY_MEMBER _CONSTEXPR20 bool empty() const noexcept {
return _Mypair._Myval2._Mysize == 0;
2019-09-05 01:57:56 +03:00
}
_CONSTEXPR20 size_type copy(
_Out_writes_(_Count) _Elem* const _Ptr, size_type _Count, const size_type _Off = 0) const {
2019-09-05 01:57:56 +03:00
// copy [_Off, _Off + _Count) to [_Ptr, _Ptr + _Count)
_Mypair._Myval2._Check_offset(_Off);
_Count = _Mypair._Myval2._Clamp_suffix_size(_Off, _Count);
_Traits::copy(_Ptr, _Mypair._Myval2._Myptr() + _Off, _Count);
return _Count;
}
_CONSTEXPR20 _Pre_satisfies_(_Dest_size >= _Count) size_type
_Copy_s(_Out_writes_all_(_Dest_size) _Elem* const _Dest, const size_type _Dest_size, size_type _Count,
const size_type _Off = 0) const {
2019-09-05 01:57:56 +03:00
// copy [_Off, _Off + _Count) to [_Dest, _Dest + _Dest_size)
_Mypair._Myval2._Check_offset(_Off);
_Count = _Mypair._Myval2._Clamp_suffix_size(_Off, _Count);
_Traits::_Copy_s(_Dest, _Dest_size, _Mypair._Myval2._Myptr() + _Off, _Count);
return _Count;
}
static _CONSTEXPR20 void _Swap_bx_large_with_small(_Scary_val& _Starts_large, _Scary_val& _Starts_small) noexcept {
2019-09-05 01:57:56 +03:00
// exchange a string in large mode with one in small mode
const pointer _Ptr = _Starts_large._Bx._Ptr;
_Starts_large._Bx._Switch_to_buf();
2019-09-05 01:57:56 +03:00
_Traits::copy(_Starts_large._Bx._Buf, _Starts_small._Bx._Buf, _BUF_SIZE);
2019-09-05 01:57:56 +03:00
_Construct_in_place(_Starts_small._Bx._Ptr, _Ptr);
}
_CONSTEXPR20 void _Swap_data(basic_string& _Right) noexcept {
using _STD swap;
auto& _My_data = _Mypair._Myval2;
auto& _Right_data = _Right._Mypair._Myval2;
#if !defined(_INSERT_STRING_ANNOTATION)
if constexpr (_Can_memcpy_val) {
#if _HAS_CXX20
if (!_STD is_constant_evaluated())
#endif // _HAS_CXX20
{
const auto _My_data_mem =
reinterpret_cast<unsigned char*>(_STD addressof(_My_data)) + _Memcpy_val_offset;
const auto _Right_data_mem =
reinterpret_cast<unsigned char*>(_STD addressof(_Right_data)) + _Memcpy_val_offset;
unsigned char _Temp_mem[_Memcpy_val_size];
_CSTD memcpy(_Temp_mem, _My_data_mem, _Memcpy_val_size);
_CSTD memcpy(_My_data_mem, _Right_data_mem, _Memcpy_val_size);
_CSTD memcpy(_Right_data_mem, _Temp_mem, _Memcpy_val_size);
return;
}
}
#endif // !defined(_INSERT_STRING_ANNOTATION)
const bool _My_large = _My_data._Large_mode_engaged();
const bool _Right_large = _Right_data._Large_mode_engaged();
if (_My_large && _Right_large) { // swap buffers, iterators preserved
swap(_My_data._Bx._Ptr, _Right_data._Bx._Ptr); // intentional ADL
} else if (_My_large) { // swap large with small
_Swap_bx_large_with_small(_My_data, _Right_data);
} else if (_Right_large) { // swap small with large
_Swap_bx_large_with_small(_Right_data, _My_data);
2019-09-05 01:57:56 +03:00
} else {
_Elem _Temp_buf[_BUF_SIZE];
_Traits::copy(_Temp_buf, _My_data._Bx._Buf, _My_data._Mysize + 1);
_Traits::copy(_My_data._Bx._Buf, _Right_data._Bx._Buf, _Right_data._Mysize + 1);
_Traits::copy(_Right_data._Bx._Buf, _Temp_buf, _My_data._Mysize + 1);
2019-09-05 01:57:56 +03:00
}
_STD swap(_My_data._Mysize, _Right_data._Mysize);
_STD swap(_My_data._Myres, _Right_data._Myres);
}
_CONSTEXPR20 void swap(basic_string& _Right) noexcept /* strengthened */ {
2019-09-05 01:57:56 +03:00
if (this != _STD addressof(_Right)) {
_Pocs(_Getal(), _Right._Getal());
#if _ITERATOR_DEBUG_LEVEL != 0
auto& _My_data = _Mypair._Myval2;
auto& _Right_data = _Right._Mypair._Myval2;
2019-09-05 01:57:56 +03:00
if (!_My_data._Large_mode_engaged()) {
_My_data._Orphan_all();
2019-09-05 01:57:56 +03:00
}
if (!_Right_data._Large_mode_engaged()) {
_Right_data._Orphan_all();
2019-09-05 01:57:56 +03:00
}
_My_data._Swap_proxy_and_iterators(_Right_data);
2019-09-05 01:57:56 +03:00
#endif // _ITERATOR_DEBUG_LEVEL != 0
_Swap_data(_Right);
}
2019-09-05 01:57:56 +03:00
}
#if _HAS_CXX17
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_NODISCARD _CONSTEXPR20 size_type find(const _StringViewIsh& _Right, const size_type _Off = 0) const
noexcept(_Is_nothrow_convertible_v<const _StringViewIsh&, basic_string_view<_Elem, _Traits>>) {
2019-09-05 01:57:56 +03:00
// look for _Right beginning at or after _Off
basic_string_view<_Elem, _Traits> _As_view = _Right;
return static_cast<size_type>(_Traits_find<_Traits>(
_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _As_view.data(), _As_view.size()));
}
#endif // _HAS_CXX17
_NODISCARD _CONSTEXPR20 size_type find(const basic_string& _Right, const size_type _Off = 0) const noexcept {
2019-09-05 01:57:56 +03:00
// look for _Right beginning at or after _Off
return static_cast<size_type>(_Traits_find<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off,
_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize));
}
_NODISCARD _CONSTEXPR20 size_type find(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off,
2019-09-05 01:57:56 +03:00
const size_type _Count) const noexcept /* strengthened */ {
// look for [_Ptr, _Ptr + _Count) beginning at or after _Off
return static_cast<size_type>(
_Traits_find<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count));
}
_NODISCARD _CONSTEXPR20 size_type find(_In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept
/* strengthened */ {
2019-09-05 01:57:56 +03:00
// look for [_Ptr, <null>) beginning at or after _Off
return static_cast<size_type>(_Traits_find<_Traits>(
_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr)));
}
_NODISCARD _CONSTEXPR20 size_type find(const _Elem _Ch, const size_type _Off = 0) const noexcept {
2019-09-05 01:57:56 +03:00
// look for _Ch at or after _Off
return static_cast<size_type>(
_Traits_find_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch));
}
#if _HAS_CXX17
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_NODISCARD _CONSTEXPR20 size_type rfind(const _StringViewIsh& _Right, const size_type _Off = npos) const
noexcept(_Is_nothrow_convertible_v<const _StringViewIsh&, basic_string_view<_Elem, _Traits>>) {
2019-09-05 01:57:56 +03:00
// look for _Right beginning before _Off
basic_string_view<_Elem, _Traits> _As_view = _Right;
return static_cast<size_type>(_Traits_rfind<_Traits>(
_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _As_view.data(), _As_view.size()));
}
#endif // _HAS_CXX17
_NODISCARD _CONSTEXPR20 size_type rfind(const basic_string& _Right, const size_type _Off = npos) const noexcept {
2019-09-05 01:57:56 +03:00
// look for _Right beginning before _Off
return static_cast<size_type>(_Traits_rfind<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off,
_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize));
}
_NODISCARD _CONSTEXPR20 size_type rfind(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off,
2019-09-05 01:57:56 +03:00
const size_type _Count) const noexcept /* strengthened */ {
// look for [_Ptr, _Ptr + _Count) beginning before _Off
return static_cast<size_type>(
_Traits_rfind<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count));
}
_NODISCARD _CONSTEXPR20 size_type rfind(_In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept
/* strengthened */ {
2019-09-05 01:57:56 +03:00
// look for [_Ptr, <null>) beginning before _Off
return static_cast<size_type>(_Traits_rfind<_Traits>(
_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr)));
}
_NODISCARD _CONSTEXPR20 size_type rfind(const _Elem _Ch, const size_type _Off = npos) const noexcept {
2019-09-05 01:57:56 +03:00
// look for _Ch before _Off
return static_cast<size_type>(
_Traits_rfind_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch));
}
#if _HAS_CXX17
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_NODISCARD _CONSTEXPR20 size_type find_first_of(const _StringViewIsh& _Right, const size_type _Off = 0) const
noexcept(_Is_nothrow_convertible_v<const _StringViewIsh&, basic_string_view<_Elem, _Traits>>) {
2019-09-05 01:57:56 +03:00
// look for one of _Right at or after _Off
basic_string_view<_Elem, _Traits> _As_view = _Right;
return static_cast<size_type>(_Traits_find_first_of<_Traits>(
_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _As_view.data(), _As_view.size()));
2019-09-05 01:57:56 +03:00
}
#endif // _HAS_CXX17
_NODISCARD _CONSTEXPR20 size_type find_first_of(
const basic_string& _Right, const size_type _Off = 0) const noexcept {
2019-09-05 01:57:56 +03:00
// look for one of _Right at or after _Off
return static_cast<size_type>(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize,
_Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize));
2019-09-05 01:57:56 +03:00
}
_NODISCARD _CONSTEXPR20 size_type find_first_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off,
const size_type _Count) const noexcept /* strengthened */ {
2019-09-05 01:57:56 +03:00
// look for one of [_Ptr, _Ptr + _Count) at or after _Off
return static_cast<size_type>(
_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count));
2019-09-05 01:57:56 +03:00
}
_NODISCARD _CONSTEXPR20 size_type find_first_of(
_In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept
/* strengthened */ {
2019-09-05 01:57:56 +03:00
// look for one of [_Ptr, <null>) at or after _Off
return static_cast<size_type>(_Traits_find_first_of<_Traits>(
_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr)));
2019-09-05 01:57:56 +03:00
}
_NODISCARD _CONSTEXPR20 size_type find_first_of(const _Elem _Ch, const size_type _Off = 0) const noexcept {
2019-09-05 01:57:56 +03:00
// look for _Ch at or after _Off
return static_cast<size_type>(
_Traits_find_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch));
}
#if _HAS_CXX17
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_NODISCARD _CONSTEXPR20 size_type find_last_of(const _StringViewIsh& _Right, const size_type _Off = npos) const
noexcept(_Is_nothrow_convertible_v<const _StringViewIsh&, basic_string_view<_Elem, _Traits>>) {
2019-09-05 01:57:56 +03:00
// look for one of _Right before _Off
basic_string_view<_Elem, _Traits> _As_view = _Right;
return static_cast<size_type>(_Traits_find_last_of<_Traits>(
_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _As_view.data(), _As_view.size()));
2019-09-05 01:57:56 +03:00
}
#endif // _HAS_CXX17
_NODISCARD _CONSTEXPR20 size_type find_last_of(const basic_string& _Right, size_type _Off = npos) const noexcept {
2019-09-05 01:57:56 +03:00
// look for one of _Right before _Off
return static_cast<size_type>(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize,
_Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize));
2019-09-05 01:57:56 +03:00
}
_NODISCARD _CONSTEXPR20 size_type find_last_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off,
const size_type _Count) const noexcept /* strengthened */ {
2019-09-05 01:57:56 +03:00
// look for one of [_Ptr, _Ptr + _Count) before _Off
return static_cast<size_type>(
_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count));
2019-09-05 01:57:56 +03:00
}
_NODISCARD _CONSTEXPR20 size_type find_last_of(
_In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ {
2019-09-05 01:57:56 +03:00
// look for one of [_Ptr, <null>) before _Off
return static_cast<size_type>(_Traits_find_last_of<_Traits>(
_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr)));
2019-09-05 01:57:56 +03:00
}
_NODISCARD _CONSTEXPR20 size_type find_last_of(const _Elem _Ch, const size_type _Off = npos) const noexcept {
2019-09-05 01:57:56 +03:00
// look for _Ch before _Off
return static_cast<size_type>(
_Traits_rfind_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch));
}
#if _HAS_CXX17
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_NODISCARD _CONSTEXPR20 size_type find_first_not_of(const _StringViewIsh& _Right, const size_type _Off = 0) const
noexcept(_Is_nothrow_convertible_v<const _StringViewIsh&, basic_string_view<_Elem, _Traits>>) {
2019-09-05 01:57:56 +03:00
// look for none of _Right at or after _Off
basic_string_view<_Elem, _Traits> _As_view = _Right;
return static_cast<size_type>(_Traits_find_first_not_of<_Traits>(
_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _As_view.data(), _As_view.size()));
2019-09-05 01:57:56 +03:00
}
#endif // _HAS_CXX17
_NODISCARD _CONSTEXPR20 size_type find_first_not_of(
const basic_string& _Right, const size_type _Off = 0) const noexcept {
2019-09-05 01:57:56 +03:00
// look for none of _Right at or after _Off
return static_cast<size_type>(_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(),
_Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize));
2019-09-05 01:57:56 +03:00
}
_NODISCARD _CONSTEXPR20 size_type find_first_not_of(_In_reads_(_Count) const _Elem* const _Ptr,
const size_type _Off, const size_type _Count) const noexcept /* strengthened */ {
2019-09-05 01:57:56 +03:00
// look for none of [_Ptr, _Ptr + _Count) at or after _Off
return static_cast<size_type>(
_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count));
2019-09-05 01:57:56 +03:00
}
_NODISCARD _CONSTEXPR20 size_type find_first_not_of(
_In_z_ const _Elem* const _Ptr, size_type _Off = 0) const noexcept
/* strengthened */ {
2019-09-05 01:57:56 +03:00
// look for one of [_Ptr, <null>) at or after _Off
return static_cast<size_type>(_Traits_find_first_not_of<_Traits>(
_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr)));
2019-09-05 01:57:56 +03:00
}
_NODISCARD _CONSTEXPR20 size_type find_first_not_of(const _Elem _Ch, const size_type _Off = 0) const noexcept {
// look for non-_Ch at or after _Off
2019-09-05 01:57:56 +03:00
return static_cast<size_type>(
_Traits_find_not_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch));
}
#if _HAS_CXX17
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_NODISCARD _CONSTEXPR20 size_type find_last_not_of(const _StringViewIsh& _Right, const size_type _Off = npos) const
noexcept(_Is_nothrow_convertible_v<const _StringViewIsh&, basic_string_view<_Elem, _Traits>>) {
2019-09-05 01:57:56 +03:00
// look for none of _Right before _Off
basic_string_view<_Elem, _Traits> _As_view = _Right;
return static_cast<size_type>(_Traits_find_last_not_of<_Traits>(
_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _As_view.data(), _As_view.size()));
2019-09-05 01:57:56 +03:00
}
#endif // _HAS_CXX17
_NODISCARD _CONSTEXPR20 size_type find_last_not_of(
const basic_string& _Right, const size_type _Off = npos) const noexcept {
2019-09-05 01:57:56 +03:00
// look for none of _Right before _Off
return static_cast<size_type>(_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(),
_Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize));
2019-09-05 01:57:56 +03:00
}
_NODISCARD _CONSTEXPR20 size_type find_last_not_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off,
const size_type _Count) const noexcept /* strengthened */ {
2019-09-05 01:57:56 +03:00
// look for none of [_Ptr, _Ptr + _Count) before _Off
return static_cast<size_type>(
_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count));
2019-09-05 01:57:56 +03:00
}
_NODISCARD _CONSTEXPR20 size_type find_last_not_of(
_In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ {
2019-09-05 01:57:56 +03:00
// look for none of [_Ptr, <null>) before _Off
return static_cast<size_type>(_Traits_find_last_not_of<_Traits>(
_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr)));
2019-09-05 01:57:56 +03:00
}
_NODISCARD _CONSTEXPR20 size_type find_last_not_of(const _Elem _Ch, const size_type _Off = npos) const noexcept {
// look for non-_Ch before _Off
2019-09-05 01:57:56 +03:00
return static_cast<size_type>(
_Traits_rfind_not_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch));
}
#if _HAS_CXX17
_NODISCARD bool _Starts_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept {
// Used exclusively by filesystem
2019-09-05 01:57:56 +03:00
return basic_string_view<_Elem, _Traits>(*this)._Starts_with(_Right);
}
#endif // _HAS_CXX17
_NODISCARD _CONSTEXPR20 basic_string substr(const size_type _Off = 0, const size_type _Count = npos)
#if _HAS_CXX23
const&
#else
const
#endif
{
// return [_Off, _Off + _Count) as new string, default-constructing its allocator
return basic_string{*this, _Off, _Count};
}
#if _HAS_CXX23
_NODISCARD constexpr basic_string substr(const size_type _Off = 0, const size_type _Count = npos) && {
// return [_Off, _Off + _Count) as new string, potentially moving, default-constructing its allocator
return basic_string{_STD move(*this), _Off, _Count};
2019-09-05 01:57:56 +03:00
}
#endif // _HAS_CXX23
2019-09-05 01:57:56 +03:00
_CONSTEXPR20 bool _Equal(const basic_string& _Right) const noexcept {
// compare [0, size()) with _Right for equality
2019-09-05 01:57:56 +03:00
return _Traits_equal<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize,
_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize);
}
_CONSTEXPR20 bool _Equal(_In_z_ const _Elem* const _Ptr) const noexcept {
// compare [0, size()) with _Ptr for equality
2019-09-05 01:57:56 +03:00
return _Traits_equal<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Ptr, _Traits::length(_Ptr));
}
#if _HAS_CXX17
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_NODISCARD _CONSTEXPR20 int compare(const _StringViewIsh& _Right) const
noexcept(_Is_nothrow_convertible_v<const _StringViewIsh&, basic_string_view<_Elem, _Traits>>) {
// compare [0, size()) with _Right
2019-09-05 01:57:56 +03:00
basic_string_view<_Elem, _Traits> _As_view = _Right;
return _Traits_compare<_Traits>(
_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _As_view.data(), _As_view.size());
}
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_NODISCARD _CONSTEXPR20 int compare(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right) const {
// compare [_Off, _Off + _Nx) with _Right
2019-09-05 01:57:56 +03:00
basic_string_view<_Elem, _Traits> _As_view = _Right;
_Mypair._Myval2._Check_offset(_Off);
return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr() + _Off, _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx),
2019-09-05 01:57:56 +03:00
_As_view.data(), _As_view.size());
}
template <class _StringViewIsh, _Is_string_view_ish<_StringViewIsh> = 0>
_NODISCARD _CONSTEXPR20 int compare(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right,
const size_type _Roff, const size_type _Count = npos) const {
// compare [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count)
2019-09-05 01:57:56 +03:00
basic_string_view<_Elem, _Traits> _As_view = _Right;
_Mypair._Myval2._Check_offset(_Off);
const auto _With_substr = _As_view.substr(_Roff, _Count);
return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr() + _Off, _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx),
2019-09-05 01:57:56 +03:00
_With_substr.data(), _With_substr.size());
}
#endif // _HAS_CXX17
_NODISCARD _CONSTEXPR20 int compare(const basic_string& _Right) const noexcept {
// compare [0, size()) with _Right
2019-09-05 01:57:56 +03:00
return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize,
_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize);
}
_NODISCARD _CONSTEXPR20 int compare(size_type _Off, size_type _Nx, const basic_string& _Right) const {
// compare [_Off, _Off + _Nx) with _Right
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Check_offset(_Off);
return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr() + _Off, _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx),
2019-09-05 01:57:56 +03:00
_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize);
}
_NODISCARD _CONSTEXPR20 int compare(const size_type _Off, const size_type _Nx, const basic_string& _Right,
const size_type _Roff, const size_type _Count = npos) const {
// compare [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count)
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Check_offset(_Off);
_Right._Mypair._Myval2._Check_offset(_Roff);
return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr() + _Off, _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx),
2019-09-05 01:57:56 +03:00
_Right._Mypair._Myval2._Myptr() + _Roff, _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count));
}
_NODISCARD _CONSTEXPR20 int compare(_In_z_ const _Elem* const _Ptr) const noexcept /* strengthened */ {
2019-09-05 01:57:56 +03:00
// compare [0, size()) with [_Ptr, <null>)
return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Ptr, _Traits::length(_Ptr));
}
_NODISCARD _CONSTEXPR20 int compare(
const size_type _Off, const size_type _Nx, _In_z_ const _Elem* const _Ptr) const {
// compare [_Off, _Off + _Nx) with [_Ptr, <null>)
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Check_offset(_Off);
return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr() + _Off, _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx),
2019-09-05 01:57:56 +03:00
_Ptr, _Traits::length(_Ptr));
}
_NODISCARD _CONSTEXPR20 int compare(const size_type _Off, const size_type _Nx,
_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Count) const {
// compare [_Off, _Off + _Nx) with [_Ptr, _Ptr + _Count)
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Check_offset(_Off);
return _Traits_compare<_Traits>(
_Mypair._Myval2._Myptr() + _Off, _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx), _Ptr, _Count);
2019-09-05 01:57:56 +03:00
}
#if _HAS_CXX20
_NODISCARD constexpr bool starts_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept {
return basic_string_view<_Elem, _Traits>{_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize}.starts_with(_Right);
2019-09-05 01:57:56 +03:00
}
_NODISCARD constexpr bool starts_with(const _Elem _Right) const noexcept {
return basic_string_view<_Elem, _Traits>{_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize}.starts_with(_Right);
2019-09-05 01:57:56 +03:00
}
_NODISCARD constexpr bool starts_with(const _Elem* const _Right) const noexcept /* strengthened */ {
return basic_string_view<_Elem, _Traits>{_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize}.starts_with(_Right);
2019-09-05 01:57:56 +03:00
}
_NODISCARD constexpr bool ends_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept {
return basic_string_view<_Elem, _Traits>{_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize}.ends_with(_Right);
2019-09-05 01:57:56 +03:00
}
_NODISCARD constexpr bool ends_with(const _Elem _Right) const noexcept {
return basic_string_view<_Elem, _Traits>{_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize}.ends_with(_Right);
2019-09-05 01:57:56 +03:00
}
_NODISCARD constexpr bool ends_with(const _Elem* const _Right) const noexcept /* strengthened */ {
return basic_string_view<_Elem, _Traits>{_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize}.ends_with(_Right);
2019-09-05 01:57:56 +03:00
}
#endif // _HAS_CXX20
#if _HAS_CXX23
_NODISCARD constexpr bool contains(const basic_string_view<_Elem, _Traits> _Right) const noexcept {
return basic_string_view<_Elem, _Traits>{_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize}.contains(_Right);
}
_NODISCARD constexpr bool contains(const _Elem _Right) const noexcept {
return basic_string_view<_Elem, _Traits>{_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize}.contains(_Right);
}
_NODISCARD constexpr bool contains(const _Elem* const _Right) const noexcept /* strengthened */ {
return basic_string_view<_Elem, _Traits>{_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize}.contains(_Right);
}
#endif // _HAS_CXX23
_NODISCARD _CONSTEXPR20 allocator_type get_allocator() const noexcept {
2019-09-05 01:57:56 +03:00
return static_cast<allocator_type>(_Getal());
}
private:
_NODISCARD static _CONSTEXPR20 size_type _Calculate_growth(
2019-09-05 01:57:56 +03:00
const size_type _Requested, const size_type _Old, const size_type _Max) noexcept {
const size_type _Masked = _Requested | _Alloc_mask;
2019-09-05 01:57:56 +03:00
if (_Masked > _Max) { // the mask overflows, settle for max_size()
return _Max;
}
if (_Old > _Max - _Old / 2) { // similarly, geometric overflows
return _Max;
}
return (_STD max)(_Masked, _Old + _Old / 2);
2019-09-05 01:57:56 +03:00
}
_NODISCARD _CONSTEXPR20 size_type _Calculate_growth(const size_type _Requested) const noexcept {
2019-09-05 01:57:56 +03:00
return _Calculate_growth(_Requested, _Mypair._Myval2._Myres, max_size());
}
template <class _Fty, class... _ArgTys>
_CONSTEXPR20 basic_string& _Reallocate_for(const size_type _New_size, _Fty _Fn, _ArgTys... _Args) {
2019-09-05 01:57:56 +03:00
// reallocate to store exactly _New_size elements, new buffer prepared by
// _Fn(_New_ptr, _New_size, _Args...)
if (_New_size > max_size()) {
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
_Xlen_string(); // result too long
2019-09-05 01:57:56 +03:00
}
const size_type _Old_capacity = _Mypair._Myval2._Myres;
size_type _New_capacity = _Calculate_growth(_New_size);
2019-09-05 01:57:56 +03:00
auto& _Al = _Getal();
const pointer _New_ptr = _Allocate_for_capacity(_Al, _New_capacity); // throws
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Orphan_all();
_ASAN_STRING_REMOVE(*this);
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Mysize = _New_size;
_Mypair._Myval2._Myres = _New_capacity;
_Fn(_Unfancy(_New_ptr), _New_size, _Args...);
if (_Old_capacity > _Small_string_capacity) {
_Deallocate_for_capacity(_Al, _Mypair._Myval2._Bx._Ptr, _Old_capacity);
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Bx._Ptr = _New_ptr;
} else {
_Construct_in_place(_Mypair._Myval2._Bx._Ptr, _New_ptr);
}
_ASAN_STRING_CREATE(*this);
2019-09-05 01:57:56 +03:00
return *this;
}
template <class _Fty, class... _ArgTys>
_CONSTEXPR20 basic_string& _Reallocate_grow_by(const size_type _Size_increase, _Fty _Fn, _ArgTys... _Args) {
2019-09-05 01:57:56 +03:00
// reallocate to increase size by _Size_increase elements, new buffer prepared by
// _Fn(_New_ptr, _Old_ptr, _Old_size, _Args...)
auto& _My_data = _Mypair._Myval2;
const size_type _Old_size = _My_data._Mysize;
if (max_size() - _Old_size < _Size_increase) {
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
_Xlen_string(); // result too long
2019-09-05 01:57:56 +03:00
}
const size_type _New_size = _Old_size + _Size_increase;
const size_type _Old_capacity = _My_data._Myres;
size_type _New_capacity = _Calculate_growth(_New_size);
2019-09-05 01:57:56 +03:00
auto& _Al = _Getal();
const pointer _New_ptr = _Allocate_for_capacity(_Al, _New_capacity); // throws
2019-09-05 01:57:56 +03:00
_My_data._Orphan_all();
_ASAN_STRING_REMOVE(*this);
2019-09-05 01:57:56 +03:00
_My_data._Mysize = _New_size;
_My_data._Myres = _New_capacity;
_Elem* const _Raw_new = _Unfancy(_New_ptr);
if (_Old_capacity > _Small_string_capacity) {
2019-09-05 01:57:56 +03:00
const pointer _Old_ptr = _My_data._Bx._Ptr;
_Fn(_Raw_new, _Unfancy(_Old_ptr), _Old_size, _Args...);
_Deallocate_for_capacity(_Al, _Old_ptr, _Old_capacity);
2019-09-05 01:57:56 +03:00
_My_data._Bx._Ptr = _New_ptr;
} else {
_Fn(_Raw_new, _My_data._Bx._Buf, _Old_size, _Args...);
_Construct_in_place(_My_data._Bx._Ptr, _New_ptr);
}
_ASAN_STRING_CREATE(*this);
2019-09-05 01:57:56 +03:00
return *this;
}
_CONSTEXPR20 void _Become_small() noexcept {
2019-09-05 01:57:56 +03:00
// release any held storage and return to small string mode
auto& _My_data = _Mypair._Myval2;
_STL_INTERNAL_CHECK(_My_data._Large_mode_engaged());
_STL_INTERNAL_CHECK(_My_data._Mysize <= _Small_string_capacity);
_My_data._Orphan_all();
_ASAN_STRING_REMOVE(*this);
const pointer _Ptr = _My_data._Bx._Ptr;
_My_data._Bx._Switch_to_buf();
_Traits::copy(_My_data._Bx._Buf, _Unfancy(_Ptr), _My_data._Mysize + 1);
auto& _Al = _Getal();
_Deallocate_for_capacity(_Al, _Ptr, _My_data._Myres);
_My_data._Myres = _Small_string_capacity;
2019-09-05 01:57:56 +03:00
}
_CONSTEXPR20 void _Eos(const size_type _New_size) noexcept { // set new length and null terminator
_ASAN_STRING_MODIFY(*this, _Mypair._Myval2._Mysize, _New_size);
_Mypair._Myval2._Mysize = _New_size;
_Traits::assign(_Mypair._Myval2._Myptr()[_New_size], _Elem());
2019-09-05 01:57:56 +03:00
}
_CONSTEXPR20 void _Tidy_deallocate() noexcept { // initialize buffer, deallocating any storage
auto& _My_data = _Mypair._Myval2;
_My_data._Orphan_all();
if (_My_data._Large_mode_engaged()) {
_ASAN_STRING_REMOVE(*this);
auto& _Al = _Getal();
_Deallocate_for_capacity(_Al, _My_data._Bx._Ptr, _My_data._Myres);
_My_data._Bx._Switch_to_buf();
2019-09-05 01:57:56 +03:00
}
_My_data._Mysize = 0;
_My_data._Myres = _Small_string_capacity;
// the _Traits::assign is last so the codegen doesn't think the char write can alias this
_Traits::assign(_My_data._Bx._Buf[0], _Elem());
2019-09-05 01:57:56 +03:00
}
public:
_CONSTEXPR20 void _Orphan_all() noexcept { // used by filesystem::path
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Orphan_all();
}
private:
_CONSTEXPR20 void _Swap_proxy_and_iterators(basic_string& _Right) noexcept {
2019-09-05 01:57:56 +03:00
_Mypair._Myval2._Swap_proxy_and_iterators(_Right._Mypair._Myval2);
}
_CONSTEXPR20 _Alty& _Getal() noexcept {
2019-09-05 01:57:56 +03:00
return _Mypair._Get_first();
}
_CONSTEXPR20 const _Alty& _Getal() const noexcept {
2019-09-05 01:57:56 +03:00
return _Mypair._Get_first();
}
_Compressed_pair<_Alty, _Scary_val> _Mypair;
};
#if _HAS_CXX17
template <class _Iter, class _Alloc = allocator<_Iter_value_t<_Iter>>,
enable_if_t<conjunction_v<_Is_iterator<_Iter>, _Is_allocator<_Alloc>>, int> = 0>
basic_string(_Iter, _Iter, _Alloc = _Alloc())
-> basic_string<_Iter_value_t<_Iter>, char_traits<_Iter_value_t<_Iter>>, _Alloc>;
2019-09-05 01:57:56 +03:00
template <class _Elem, class _Traits, class _Alloc = allocator<_Elem>,
enable_if_t<_Is_allocator<_Alloc>::value, int> = 0>
explicit basic_string(basic_string_view<_Elem, _Traits>, const _Alloc& = _Alloc())
-> basic_string<_Elem, _Traits, _Alloc>;
2019-09-05 01:57:56 +03:00
template <class _Elem, class _Traits, class _Alloc = allocator<_Elem>,
enable_if_t<_Is_allocator<_Alloc>::value, int> = 0>
basic_string(basic_string_view<_Elem, _Traits>, _Guide_size_type_t<_Alloc>, _Guide_size_type_t<_Alloc>,
const _Alloc& = _Alloc()) -> basic_string<_Elem, _Traits, _Alloc>;
#if _HAS_CXX23
template <_RANGES input_range _Rng, _Allocator_for_container _Alloc = allocator<_RANGES range_value_t<_Rng>>>
basic_string(from_range_t, _Rng&&, _Alloc = _Alloc())
-> basic_string<_RANGES range_value_t<_Rng>, char_traits<_RANGES range_value_t<_Rng>>, _Alloc>;
#endif // _HAS_CXX23
2019-09-05 01:57:56 +03:00
#endif // _HAS_CXX17
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
_CONSTEXPR20 void swap(basic_string<_Elem, _Traits, _Alloc>& _Left,
basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept /* strengthened */ {
2019-09-05 01:57:56 +03:00
_Left.swap(_Right);
}
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
_NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+(
2019-09-05 01:57:56 +03:00
const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) {
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
const auto _Left_size = _Left.size();
const auto _Right_size = _Right.size();
if (_Left.max_size() - _Left_size < _Right_size) {
_Xlen_string();
}
return {_String_constructor_concat_tag{}, _Left, _Left.c_str(), _Left_size, _Right.c_str(), _Right_size};
2019-09-05 01:57:56 +03:00
}
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
_NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+(
2019-09-05 01:57:56 +03:00
_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) {
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
using _Size_type = typename basic_string<_Elem, _Traits, _Alloc>::size_type;
const auto _Left_size = _Convert_size<_Size_type>(_Traits::length(_Left));
const auto _Right_size = _Right.size();
if (_Right.max_size() - _Right_size < _Left_size) {
_Xlen_string();
}
return {_String_constructor_concat_tag{}, _Right, _Left, _Left_size, _Right.c_str(), _Right_size};
2019-09-05 01:57:56 +03:00
}
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
_NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+(
2019-09-05 01:57:56 +03:00
const _Elem _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) {
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
const auto _Right_size = _Right.size();
if (_Right_size == _Right.max_size()) {
_Xlen_string();
}
return {_String_constructor_concat_tag{}, _Right, _STD addressof(_Left), 1, _Right.c_str(), _Right_size};
2019-09-05 01:57:56 +03:00
}
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
_NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+(
2019-09-05 01:57:56 +03:00
const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) {
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
using _Size_type = typename basic_string<_Elem, _Traits, _Alloc>::size_type;
const auto _Left_size = _Left.size();
const auto _Right_size = _Convert_size<_Size_type>(_Traits::length(_Right));
if (_Left.max_size() - _Left_size < _Right_size) {
_Xlen_string();
}
return {_String_constructor_concat_tag{}, _Left, _Left.c_str(), _Left_size, _Right, _Right_size};
2019-09-05 01:57:56 +03:00
}
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
_NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+(
2019-09-05 01:57:56 +03:00
const basic_string<_Elem, _Traits, _Alloc>& _Left, const _Elem _Right) {
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
const auto _Left_size = _Left.size();
if (_Left_size == _Left.max_size()) {
_Xlen_string();
}
return {_String_constructor_concat_tag{}, _Left, _Left.c_str(), _Left_size, _STD addressof(_Right), 1};
2019-09-05 01:57:56 +03:00
}
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
_NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+(
2019-09-05 01:57:56 +03:00
const basic_string<_Elem, _Traits, _Alloc>& _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) {
return _STD move(_Right.insert(0, _Left));
}
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
_NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+(
2019-09-05 01:57:56 +03:00
basic_string<_Elem, _Traits, _Alloc>&& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) {
return _STD move(_Left.append(_Right));
}
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
_NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+(
2019-09-05 01:57:56 +03:00
basic_string<_Elem, _Traits, _Alloc>&& _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) {
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
#if _ITERATOR_DEBUG_LEVEL == 2
_STL_VERIFY(_STD addressof(_Left) != _STD addressof(_Right),
"You cannot concatenate the same moved string to itself. See N4950 [res.on.arguments]/1.3: "
"If a function argument is bound to an rvalue reference parameter, the implementation may assume that "
"this parameter is a unique reference to this argument, except that the argument passed to "
"a move-assignment operator may be a reference to *this ([lib.types.movedfrom]).");
Avoid double strlen for string operator+ and implement P1165R1 (#467) Resolves GH-53. Resolves GH-456. Co-authored by: @barcharcraz Co-authored by: @ArtemSarmini This change adds a bespoke constructor to `basic_string` to handle string concat use cases, removing any EH states we previously emitted in our operator+s, avoiding double strlen in our operator+s, The EH states problem comes from our old pattern: ``` S operator+(a, b) { S result; result.reserve(a.size() +b.size()); // throws result += a; // throws result += b; // throws return result; } ``` Here, the compiler does not know that the append operation can't throw, because it doesn't understand `basic_string` and doesn't know the `reserve` has made that always safe. As a result, the compiler emitted EH handing code to call `result`'s destructor after each of the reserve and `operator+=` calls. Using a bespoke concatenating constructor avoids these problems because there is only one throwing operation (in IDL0 mode). As expected, this results in a small performance win in all concats due to avoiding needing to set up EH stuff, and a large performance win for the `const char*` concats due to the avoided second `strlen`: Performance: ``` #include <benchmark/benchmark.h> #include <stdint.h> #include <string> constexpr size_t big = 2 << 12; constexpr size_t multiplier = 64; static void string_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_ntbs(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); std::string yBuf(static_cast<size_t>(state.range(1)), 'b'); const char *const y = yBuf.c_str(); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(string_concat_ntbs)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void string_concat_char(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + 'b'); } } BENCHMARK(string_concat_char)->Range(2, big); static void ntbs_concat_string(benchmark::State &state) { std::string xBuf(static_cast<size_t>(state.range(0)), 'a'); const char *const x = xBuf.c_str(); std::string y(static_cast<size_t>(state.range(1)), 'b'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize(x + y); } } BENCHMARK(ntbs_concat_string)->RangeMultiplier(multiplier)->Ranges({{2, big}, {2, big}}); static void char_concat_string(benchmark::State &state) { std::string x(static_cast<size_t>(state.range(0)), 'a'); for (auto _ : state) { (void)_; benchmark::DoNotOptimize('b' + x); } } BENCHMARK(char_concat_string)->Range(2, big); BENCHMARK_MAIN(); ``` Times are in NS on a Ryzen Threadripper 3970X, improvements are `((Old/New)-1)*100` | | old x64 | new x64 | improvement | old x86 | new x86 | improvement | | ------------------------------- | ------- | ------- | ----------- | ------- |-------- | ----------- | | string_concat_string/2/2 | 12.8697 | 5.78125 | 122.61% | 13.9029 | 11.0696 | 25.60% | | string_concat_string/64/2 | 62.779 | 61.3839 | 2.27% | 66.4394 | 61.6296 | 7.80% | | string_concat_string/4096/2 | 125.558 | 124.512 | 0.84% | 124.477 | 117.606 | 5.84% | | string_concat_string/8192/2 | 188.337 | 184.152 | 2.27% | 189.982 | 185.598 | 2.36% | | string_concat_string/2/64 | 64.5229 | 64.1741 | 0.54% | 67.1338 | 61.4962 | 9.17% | | string_concat_string/64/64 | 65.5692 | 59.9888 | 9.30% | 66.7742 | 60.4781 | 10.41% | | string_concat_string/4096/64 | 122.768 | 122.768 | 0.00% | 126.774 | 116.327 | 8.98% | | string_concat_string/8192/64 | 190.43 | 181.362 | 5.00% | 188.516 | 186.234 | 1.23% | | string_concat_string/2/4096 | 125.558 | 119.978 | 4.65% | 120.444 | 111.524 | 8.00% | | string_concat_string/64/4096 | 125.558 | 119.978 | 4.65% | 122.911 | 117.136 | 4.93% | | string_concat_string/4096/4096 | 188.337 | 184.152 | 2.27% | 193.337 | 182.357 | 6.02% | | string_concat_string/8192/4096 | 273.438 | 266.811 | 2.48% | 267.656 | 255.508 | 4.75% | | string_concat_string/2/8192 | 205.078 | 194.964 | 5.19% | 175.025 | 170.181 | 2.85% | | string_concat_string/64/8192 | 205.078 | 188.337 | 8.89% | 191.676 | 183.06 | 4.71% | | string_concat_string/4096/8192 | 266.811 | 256.696 | 3.94% | 267.455 | 255.221 | 4.79% | | string_concat_string/8192/8192 | 414.69 | 435.965 | -4.88% | 412.784 | 403.01 | 2.43% | | string_concat_ntbs/2/2 | 12.8348 | 5.9375 | 116.17% | 14.74 | 11.132 | 32.41% | | string_concat_ntbs/64/2 | 71.1496 | 59.375 | 19.83% | 70.6934 | 60.9371 | 16.01% | | string_concat_ntbs/4096/2 | 128.697 | 114.397 | 12.50% | 126.626 | 121.887 | 3.89% | | string_concat_ntbs/8192/2 | 194.964 | 176.479 | 10.47% | 196.641 | 186.88 | 5.22% | | string_concat_ntbs/2/64 | 100.446 | 74.986 | 33.95% | 109.082 | 83.3939 | 30.80% | | string_concat_ntbs/64/64 | 106.027 | 78.4738 | 35.11% | 109.589 | 84.3635 | 29.90% | | string_concat_ntbs/4096/64 | 164.969 | 138.114 | 19.44% | 165.417 | 142.116 | 16.40% | | string_concat_ntbs/8192/64 | 224.958 | 200.195 | 12.37% | 228.769 | 200.347 | 14.19% | | string_concat_ntbs/2/4096 | 2040.32 | 1074.22 | 89.94% | 2877.33 | 1362.74 | 111.14% | | string_concat_ntbs/64/4096 | 1994.98 | 1074.22 | 85.71% | 2841.93 | 1481.62 | 91.81% | | string_concat_ntbs/4096/4096 | 2050.78 | 1147.46 | 78.72% | 2907.78 | 1550.82 | 87.50% | | string_concat_ntbs/8192/4096 | 2148.44 | 1227.68 | 75.00% | 2966.92 | 1583.78 | 87.33% | | string_concat_ntbs/2/8192 | 3934.14 | 2099.61 | 87.37% | 5563.32 | 2736.56 | 103.30% | | string_concat_ntbs/64/8192 | 3989.95 | 1994.98 | 100.00% | 5456.84 | 2823.53 | 93.26% | | string_concat_ntbs/4096/8192 | 4049.24 | 2197.27 | 84.29% | 5674.02 | 2957.04 | 91.88% | | string_concat_ntbs/8192/8192 | 4237.58 | 2249.58 | 88.37% | 5755.07 | 3095.65 | 85.91% | | string_concat_char/2 | 12.8348 | 3.44936 | 272.09% | 11.1104 | 10.6976 | 3.86% | | string_concat_char/8 | 8.99833 | 3.45285 | 160.61% | 11.1964 | 10.6928 | 4.71% | | string_concat_char/64 | 65.5692 | 60.9375 | 7.60% | 65.7585 | 60.0182 | 9.56% | | string_concat_char/512 | 72.5446 | 69.7545 | 4.00% | 83.952 | 79.5254 | 5.57% | | string_concat_char/4096 | 125.558 | 119.978 | 4.65% | 123.475 | 117.103 | 5.44% | | string_concat_char/8192 | 190.43 | 187.988 | 1.30% | 189.181 | 185.174 | 2.16% | | ntbs_concat_string/2/2 | 13.4975 | 6.13839 | 119.89% | 14.8623 | 11.09 | 34.02% | | ntbs_concat_string/64/2 | 104.98 | 79.5201 | 32.02% | 112.207 | 83.7111 | 34.04% | | ntbs_concat_string/4096/2 | 2085.66 | 1098.63 | 89.84% | 2815.19 | 1456.08 | 93.34% | | ntbs_concat_string/8192/2 | 3899.27 | 2099.61 | 85.71% | 5544.52 | 2765.16 | 100.51% | | ntbs_concat_string/2/64 | 71.4983 | 62.779 | 13.89% | 72.6602 | 63.1953 | 14.98% | | ntbs_concat_string/64/64 | 104.98 | 80.2176 | 30.87% | 111.073 | 81.8413 | 35.72% | | ntbs_concat_string/4096/64 | 2085.66 | 1074.22 | 94.16% | 2789.73 | 1318.7 | 111.55% | | ntbs_concat_string/8192/64 | 3989.95 | 2085.66 | 91.30% | 5486.85 | 2693.83 | 103.68% | | ntbs_concat_string/2/4096 | 136.719 | 128.348 | 6.52% | 122.605 | 114.44 | 7.13% | | ntbs_concat_string/64/4096 | 167.411 | 142.997 | 17.07% | 168.572 | 138.566 | 21.65% | | ntbs_concat_string/4096/40 | 2099.61 | 1171.88 | 79.17% | 2923.85 | 1539.02 | 89.98% | | ntbs_concat_string/8192/40 | 4098.07 | 2246.09 | 82.45% | 5669.34 | 3005.25 | 88.65% | | ntbs_concat_string/2/8192 | 213.1 | 199.498 | 6.82% | 178.197 | 168.532 | 5.73% | | ntbs_concat_string/64/8192 | 223.214 | 214.844 | 3.90% | 232.263 | 203.722 | 14.01% | | ntbs_concat_string/4096/81 | 2148.44 | 1255.58 | 71.11% | 2980.78 | 1612.97 | 84.80% | | ntbs_concat_string/8192/81 | 4237.58 | 2406.53 | 76.09% | 5775.55 | 3067.94 | 88.25% | | char_concat_string/2 | 11.1607 | 3.60631 | 209.48% | 11.2101 | 10.7192 | 4.58% | | char_concat_string/8 | 11.4746 | 3.52958 | 225.10% | 11.4595 | 10.709 | 7.01% | | char_concat_string/64 | 65.5692 | 66.9643 | -2.08% | 66.6272 | 60.8601 | 9.48% | | char_concat_string/512 | 68.0106 | 73.2422 | -7.14% | 91.1946 | 83.0791 | 9.77% | | char_concat_string/4096 | 125.558 | 122.768 | 2.27% | 119.432 | 110.031 | 8.54% | | char_concat_string/8192 | 199.498 | 199.498 | 0.00% | 171.895 | 169.173 | 1.61% | Code size: ``` #include <string> std::string strings(const std::string& a, const std::string& b) { return a + b; } std::string string_ntbs(const std::string& a, const char * b) { return a + b; } std::string string_char(const std::string& a, char b) { return a + b; } std::string ntbs_string(const char * a, const std::string& b) { return a + b; } std::string char_string(char a, const std::string& b) { return a + b; } ``` Sizes are in bytes for the `.obj`, "Times Original" is New/Old, `cl /EHsc /W4 /WX /c /O2 .\code_size.cpp`: | Bytes | Before | After | Times Original | | ----- | ------ | ------ | -------------- | | x64 | 70,290 | 34,192 | 0.486 | | x86 | 47,152 | 28,792 | 0.611 |
2020-02-01 03:45:39 +03:00
#endif // _ITERATOR_DEBUG_LEVEL == 2
return {_String_constructor_concat_tag{}, _Left, _Right};
2019-09-05 01:57:56 +03:00
}
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
_NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+(
2019-09-05 01:57:56 +03:00
_In_z_ const _Elem* const _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) {
return _STD move(_Right.insert(0, _Left));
}
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
_NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+(
2019-09-05 01:57:56 +03:00
const _Elem _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) {
return _STD move(_Right.insert(0, 1, _Left));
}
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
_NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+(
2019-09-05 01:57:56 +03:00
basic_string<_Elem, _Traits, _Alloc>&& _Left, _In_z_ const _Elem* const _Right) {
return _STD move(_Left.append(_Right));
}
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
_NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+(
2019-09-05 01:57:56 +03:00
basic_string<_Elem, _Traits, _Alloc>&& _Left, const _Elem _Right) {
_Left.push_back(_Right);
return _STD move(_Left);
}
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
_NODISCARD _CONSTEXPR20 bool operator==(
2019-09-05 01:57:56 +03:00
const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept {
return _Left._Equal(_Right);
}
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
_NODISCARD _CONSTEXPR20 bool operator==(
const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) noexcept /* strengthened */ {
return _Left._Equal(_Right);
2019-09-05 01:57:56 +03:00
}
#if _HAS_CXX20
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
_NODISCARD constexpr _Get_comparison_category_t<_Traits> operator<=>(
const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept {
return static_cast<_Get_comparison_category_t<_Traits>>(_Left.compare(_Right) <=> 0);
}
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
_NODISCARD constexpr _Get_comparison_category_t<_Traits> operator<=>(
const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) noexcept /* strengthened */ {
return static_cast<_Get_comparison_category_t<_Traits>>(_Left.compare(_Right) <=> 0);
}
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
template <class _Elem, class _Traits, class _Alloc>
_NODISCARD bool operator==(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept
/* strengthened */ {
return _Right._Equal(_Left);
2019-09-05 01:57:56 +03:00
}
template <class _Elem, class _Traits, class _Alloc>
_NODISCARD bool operator!=(
const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept {
return !(_Left == _Right);
}
template <class _Elem, class _Traits, class _Alloc>
_NODISCARD bool operator!=(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept
/* strengthened */ {
2019-09-05 01:57:56 +03:00
return !(_Left == _Right);
}
template <class _Elem, class _Traits, class _Alloc>
_NODISCARD bool operator!=(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) noexcept
/* strengthened */ {
2019-09-05 01:57:56 +03:00
return !(_Left == _Right);
}
template <class _Elem, class _Traits, class _Alloc>
_NODISCARD bool operator<(
const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept {
return _Left.compare(_Right) < 0;
}
template <class _Elem, class _Traits, class _Alloc>
_NODISCARD bool operator<(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept
/* strengthened */ {
2019-09-05 01:57:56 +03:00
return _Right.compare(_Left) > 0;
}
template <class _Elem, class _Traits, class _Alloc>
_NODISCARD bool operator<(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) noexcept
/* strengthened */ {
2019-09-05 01:57:56 +03:00
return _Left.compare(_Right) < 0;
}
template <class _Elem, class _Traits, class _Alloc>
_NODISCARD bool operator>(
const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept {
return _Right < _Left;
}
template <class _Elem, class _Traits, class _Alloc>
_NODISCARD bool operator>(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept
/* strengthened */ {
2019-09-05 01:57:56 +03:00
return _Right < _Left;
}
template <class _Elem, class _Traits, class _Alloc>
_NODISCARD bool operator>(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) noexcept
/* strengthened */ {
2019-09-05 01:57:56 +03:00
return _Right < _Left;
}
template <class _Elem, class _Traits, class _Alloc>
_NODISCARD bool operator<=(
const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept {
return !(_Right < _Left);
}
template <class _Elem, class _Traits, class _Alloc>
_NODISCARD bool operator<=(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept
/* strengthened */ {
2019-09-05 01:57:56 +03:00
return !(_Right < _Left);
}
template <class _Elem, class _Traits, class _Alloc>
_NODISCARD bool operator<=(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) noexcept
/* strengthened */ {
2019-09-05 01:57:56 +03:00
return !(_Right < _Left);
}
template <class _Elem, class _Traits, class _Alloc>
_NODISCARD bool operator>=(
const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept {
return !(_Left < _Right);
}
template <class _Elem, class _Traits, class _Alloc>
_NODISCARD bool operator>=(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept
/* strengthened */ {
2019-09-05 01:57:56 +03:00
return !(_Left < _Right);
}
template <class _Elem, class _Traits, class _Alloc>
_NODISCARD bool operator>=(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) noexcept
/* strengthened */ {
2019-09-05 01:57:56 +03:00
return !(_Left < _Right);
}
#endif // ^^^ !_HAS_CXX20 ^^^
2019-09-05 01:57:56 +03:00
_EXPORT_STD using string = basic_string<char, char_traits<char>, allocator<char>>;
_EXPORT_STD using wstring = basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t>>;
2019-09-05 01:57:56 +03:00
#ifdef __cpp_lib_char8_t
_EXPORT_STD using u8string = basic_string<char8_t, char_traits<char8_t>, allocator<char8_t>>;
#endif // defined(__cpp_lib_char8_t)
_EXPORT_STD using u16string = basic_string<char16_t, char_traits<char16_t>, allocator<char16_t>>;
_EXPORT_STD using u32string = basic_string<char32_t, char_traits<char32_t>, allocator<char32_t>>;
2019-09-05 01:57:56 +03:00
template <class _Elem, class _Alloc>
struct hash<basic_string<_Elem, char_traits<_Elem>, _Alloc>>
: _Conditionally_enabled_hash<basic_string<_Elem, char_traits<_Elem>, _Alloc>, _Is_EcharT<_Elem>> {
_NODISCARD static size_t _Do_hash(const basic_string<_Elem, char_traits<_Elem>, _Alloc>& _Keyval) noexcept {
2019-09-05 01:57:56 +03:00
return _Hash_array_representation(_Keyval.c_str(), _Keyval.size());
}
};
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
2019-09-05 01:57:56 +03:00
basic_istream<_Elem, _Traits>& operator>>(
basic_istream<_Elem, _Traits>& _Istr, basic_string<_Elem, _Traits, _Alloc>& _Str) {
2019-09-05 01:57:56 +03:00
using _Myis = basic_istream<_Elem, _Traits>;
using _Ctype = typename _Myis::_Ctype;
using _Mystr = basic_string<_Elem, _Traits, _Alloc>;
using _Mysizt = typename _Mystr::size_type;
typename _Myis::iostate _State = _Myis::goodbit;
bool _Changed = false;
const typename _Myis::sentry _Ok(_Istr);
if (_Ok) { // state okay, extract characters
const _Ctype& _Ctype_fac = _STD use_facet<_Ctype>(_Istr.getloc());
_Str.erase();
_TRY_IO_BEGIN
_Mysizt _Size;
if (0 < _Istr.width() && static_cast<_Mysizt>(_Istr.width()) < _Str.max_size()) {
_Size = static_cast<_Mysizt>(_Istr.width());
} else {
_Size = _Str.max_size();
}
typename _Traits::int_type _Meta = _Istr.rdbuf()->sgetc();
for (; 0 < _Size; --_Size, _Meta = _Istr.rdbuf()->snextc()) {
if (_Traits::eq_int_type(_Traits::eof(), _Meta)) { // end of file, quit
_State |= _Myis::eofbit;
break;
} else if (_Ctype_fac.is(_Ctype::space, _Traits::to_char_type(_Meta))) {
break; // whitespace, quit
} else { // add character to string
_Str.push_back(_Traits::to_char_type(_Meta));
_Changed = true;
}
}
_CATCH_IO_(_Myis, _Istr)
}
_Istr.width(0);
if (!_Changed) {
_State |= _Myis::failbit;
}
_Istr.setstate(_State);
return _Istr;
2019-09-05 01:57:56 +03:00
}
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc>
2019-09-05 01:57:56 +03:00
basic_ostream<_Elem, _Traits>& operator<<(
basic_ostream<_Elem, _Traits>& _Ostr, const basic_string<_Elem, _Traits, _Alloc>& _Str) {
return _Insert_string(_Ostr, _Str.data(), _Str.size());
}
inline namespace literals {
inline namespace string_literals {
_EXPORT_STD _NODISCARD _CONSTEXPR20 string operator""s(const char* _Str, size_t _Len) {
2022-06-20 03:42:13 +03:00
return string{_Str, _Len};
2019-09-05 01:57:56 +03:00
}
_EXPORT_STD _NODISCARD _CONSTEXPR20 wstring operator""s(const wchar_t* _Str, size_t _Len) {
2022-06-20 03:42:13 +03:00
return wstring{_Str, _Len};
2019-09-05 01:57:56 +03:00
}
#ifdef __cpp_char8_t
_EXPORT_STD _NODISCARD _CONSTEXPR20 basic_string<char8_t> operator""s(const char8_t* _Str, size_t _Len) {
2022-06-20 03:42:13 +03:00
return basic_string<char8_t>{_Str, _Len};
2019-09-05 01:57:56 +03:00
}
#endif // defined(__cpp_char8_t)
2019-09-05 01:57:56 +03:00
_EXPORT_STD _NODISCARD _CONSTEXPR20 u16string operator""s(const char16_t* _Str, size_t _Len) {
2022-06-20 03:42:13 +03:00
return u16string{_Str, _Len};
2019-09-05 01:57:56 +03:00
}
_EXPORT_STD _NODISCARD _CONSTEXPR20 u32string operator""s(const char32_t* _Str, size_t _Len) {
2022-06-20 03:42:13 +03:00
return u32string{_Str, _Len};
2019-09-05 01:57:56 +03:00
}
} // namespace string_literals
} // namespace literals
#if _HAS_CXX20
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc, class _Uty>
constexpr basic_string<_Elem, _Traits, _Alloc>::size_type erase(
basic_string<_Elem, _Traits, _Alloc>& _Cont, const _Uty& _Val) {
return _STD _Erase_remove(_Cont, _Val);
}
_EXPORT_STD template <class _Elem, class _Traits, class _Alloc, class _Pr>
constexpr basic_string<_Elem, _Traits, _Alloc>::size_type erase_if(
basic_string<_Elem, _Traits, _Alloc>& _Cont, _Pr _Pred) {
return _STD _Erase_remove_if(_Cont, _STD _Pass_fn(_Pred));
}
#endif // _HAS_CXX20
2019-09-05 01:57:56 +03:00
#if _HAS_CXX17
namespace pmr {
_EXPORT_STD template <class _Elem, class _Traits = char_traits<_Elem>>
2019-09-05 01:57:56 +03:00
using basic_string = _STD basic_string<_Elem, _Traits, polymorphic_allocator<_Elem>>;
_EXPORT_STD using string = basic_string<char>;
2019-09-05 01:57:56 +03:00
#ifdef __cpp_lib_char8_t
_EXPORT_STD using u8string = basic_string<char8_t>;
#endif // defined(__cpp_lib_char8_t)
_EXPORT_STD using u16string = basic_string<char16_t>;
_EXPORT_STD using u32string = basic_string<char32_t>;
_EXPORT_STD using wstring = basic_string<wchar_t>;
2019-09-05 01:57:56 +03:00
} // namespace pmr
#endif // _HAS_CXX17
_STD_END
#undef _ASAN_STRING_REMOVE
#undef _ASAN_STRING_CREATE
#undef _ASAN_STRING_MODIFY
#undef _INSERT_STRING_ANNOTATION
2019-09-05 01:57:56 +03:00
#pragma pop_macro("new")
_STL_RESTORE_CLANG_WARNINGS
#pragma warning(pop)
#pragma pack(pop)
#endif // _STL_COMPILER_PREPROCESSOR
#endif // _XSTRING_