Skip to content

Commit a8a9c8e

Browse files
committed
[libc++] Optimize / partially inline basic_string copy constructor
Splits copy constructor up inlining short initialization, outlining long initialization into __init_long() which is the externally instantiated slow path initialization. Subsequently changing the copy ctor to be inlined (not externally instantiated) provides significant speed ups for short string initialization. Generated code given: void StringCopyCtor(void* mem, const std::string& s) { std::string*p = new(mem) std::string{s}; } asm: cmp byte ptr [rsi + 23], 0 js .LBB0_2 mov rax, qword ptr [rsi + 16] mov qword ptr [rdi + 16], rax movups xmm0, xmmword ptr [rsi] movups xmmword ptr [rdi], xmm0 ret .LBB0_2: jmp std::basic_string::__init_long # TAILCALL Benchmark: BM_StringCopy_Empty 5.19ns ± 6% 1.50ns ± 8% -71.02% (p=0.000 n=10+10) BM_StringCopy_Small 5.14ns ± 8% 1.53ns ± 7% -70.17% (p=0.000 n=10+10) BM_StringCopy_Large 18.9ns ± 0% 19.3ns ± 0% +1.92% (p=0.000 n=10+10) BM_StringCopy_Huge 309ns ± 1% 316ns ± 5% ~ (p=0.633 n=8+10) Patch from Martijn Vels ([email protected]) Reviewed as D72160.
1 parent cd40bd0 commit a8a9c8e

File tree

1 file changed

+19
-2
lines changed

1 file changed

+19
-2
lines changed

libcxx/include/string

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,6 +1549,11 @@ private:
15491549
inline
15501550
void __init(size_type __n, value_type __c);
15511551

1552+
// Identical to __init(s, sz), except that this function is always
1553+
// externally instantiated and not inlined: this function is the
1554+
// slow path for the (inlined) copy constructor.
1555+
void __init_long_external(const value_type* __s, size_type __sz);
1556+
15521557
template <class _InputIterator>
15531558
inline
15541559
_EnableIf
@@ -1797,6 +1802,18 @@ basic_string<_CharT, _Traits, _Allocator>::__init(const value_type* __s, size_ty
17971802
traits_type::assign(__p[__sz], value_type());
17981803
}
17991804

1805+
template <class _CharT, class _Traits, class _Allocator>
1806+
void basic_string<_CharT, _Traits, _Allocator>::__init_long_external(
1807+
const _CharT* __s, size_type __sz) {
1808+
size_type __cap = __recommend(__sz);
1809+
pointer __p = __alloc_traits::allocate(__alloc(), __cap + 1);
1810+
__set_long_pointer(__p);
1811+
__set_long_cap(__cap + 1);
1812+
__set_long_size(__sz);
1813+
traits_type::copy(_VSTD::__to_address(__p), __s, __sz);
1814+
traits_type::assign(__p[__sz], value_type());
1815+
}
1816+
18001817
template <class _CharT, class _Traits, class _Allocator>
18011818
template <class>
18021819
basic_string<_CharT, _Traits, _Allocator>::basic_string(const _CharT* __s, const _Allocator& __a)
@@ -1840,7 +1857,7 @@ basic_string<_CharT, _Traits, _Allocator>::basic_string(const basic_string& __st
18401857
if (!__str.__is_long())
18411858
__r_.first().__r = __str.__r_.first().__r;
18421859
else
1843-
__init(_VSTD::__to_address(__str.__get_long_pointer()), __str.__get_long_size());
1860+
__init_long_external(_VSTD::__to_address(__str.__get_long_pointer()), __str.__get_long_size());
18441861
#if _LIBCPP_DEBUG_LEVEL >= 2
18451862
__get_db()->__insert_c(this);
18461863
#endif
@@ -1854,7 +1871,7 @@ basic_string<_CharT, _Traits, _Allocator>::basic_string(
18541871
if (!__str.__is_long())
18551872
__r_.first().__r = __str.__r_.first().__r;
18561873
else
1857-
__init(_VSTD::__to_address(__str.__get_long_pointer()), __str.__get_long_size());
1874+
__init_long_external(_VSTD::__to_address(__str.__get_long_pointer()), __str.__get_long_size());
18581875
#if _LIBCPP_DEBUG_LEVEL >= 2
18591876
__get_db()->__insert_c(this);
18601877
#endif

0 commit comments

Comments
 (0)