-
Notifications
You must be signed in to change notification settings - Fork 14.2k
Improve string allocation #92652
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Improve string allocation #92652
Conversation
* Remove unnecessary std::max in shrink_to_fit * Remove unnecessary condition in __recommend * Fix allocation for string with small_size required less bytes (24 instead of 26, 12 instead of 11, etc) ``` #include <cstddef> #include <cstdint> using size_type = size_t; template <typename long_type> struct __long { long_type x; long_type y; long_type z; }; template <typename value_type, typename long_type> static constexpr size_type min_cap = (sizeof(__long<long_type>) - 1) / sizeof(value_type) > 2 ? (sizeof(__long<long_type>) - 1) / sizeof(value_type) : 2; template <typename long_type> static constexpr size_type alignment = sizeof(long_type); template <size_type __a> static constexpr size_type __align_it(size_type __s) noexcept { return (__s + (__a - 1)) & ~(__a - 1); } template <typename value_type, typename long_type, size_type __endian_factor> static constexpr size_type __recommend(size_type __s) noexcept { constexpr auto __min_cap = min_cap<value_type, long_type>; constexpr auto __alignment = alignment<long_type>; if (__s < __min_cap) { return static_cast<size_type>(__min_cap) - 1; } const size_type __boundary = sizeof(value_type) < __alignment ? __alignment / sizeof(value_type) : __endian_factor; size_type __guess = __align_it<__boundary>(__s + 1) - 1; //if (__guess == __min_cap) __guess += __endian_factor; return __guess; } template <typename value_type, typename long_type, size_type __endian_factor> static constexpr auto to_alloc(size_type s) { return __recommend<value_type, long_type, __endian_factor>(s) + 1; } int main() { static constexpr auto char8_64 = min_cap<char, long>; static_assert(char8_64 == 23); static constexpr auto char16_64 = min_cap<char16_t, long>; static_assert(char16_64 == 11); static constexpr auto char32_64 = min_cap<char32_t, long>; static_assert(char32_64 == 5); static constexpr auto char64_64 = min_cap<long, long>; static_assert(char64_64 == 2); static constexpr auto char8_32 = min_cap<char, int>; static_assert(char8_32 == 11); static constexpr auto char16_32 = min_cap<char16_t, int>; static_assert(char16_32 == 5); static constexpr auto char32_32 = min_cap<char32_t, int>; static_assert(char32_32 == 2); static constexpr auto char64_32 = min_cap<long, int>; static_assert(char64_32 == 2); // 64 bit system, __endian_factor = 1 static_assert(to_alloc<char, long, 1>(0) == char8_64); static_assert(to_alloc<char16_t, long, 1>(0) == char16_64); static_assert(to_alloc<char32_t, long, 1>(0) == char32_64); static_assert(to_alloc<long, long, 1>(0) == char64_64); static_assert(to_alloc<char, long, 1>(char8_64) == char8_64 + 1); static_assert(to_alloc<char16_t, long, 1>(char16_64) == char16_64 + 1); static_assert(to_alloc<char32_t, long, 1>(char32_64) == char32_64 + 1); static_assert(to_alloc<long, long, 1>(char64_64) == char64_64 + 1); static_assert(to_alloc<char, long, 1>(char8_64 + 1) == 32); static_assert(to_alloc<char16_t, long, 1>(char16_64 + 1) == 16); static_assert(to_alloc<char32_t, long, 1>(char32_64 + 1) == 8); static_assert(to_alloc<long, long, 1>(char64_64 + 1) == 4); // 64 bit system, __endian_factor = 2 static_assert(to_alloc<char, long, 2>(0) == char8_64); static_assert(to_alloc<char16_t, long, 2>(0) == char16_64); static_assert(to_alloc<char32_t, long, 2>(0) == char32_64); static_assert(to_alloc<long, long, 2>(0) == char64_64); static_assert(to_alloc<char, long, 2>(char8_64) == char8_64 + 1); static_assert(to_alloc<char16_t, long, 2>(char16_64) == char16_64 + 1); static_assert(to_alloc<char32_t, long, 2>(char32_64) == char32_64 + 1); static_assert(to_alloc<long, long, 2>(char64_64) == char64_64 + 2); static_assert(to_alloc<char, long, 2>(char8_64 + 1) == 32); static_assert(to_alloc<char16_t, long, 2>(char16_64 + 1) == 16); static_assert(to_alloc<char32_t, long, 2>(char32_64 + 1) == 8); static_assert(to_alloc<long, long, 2>(char64_64 + 1) == 4); // 32 bit system, __endian_factor = 1 static_assert(to_alloc<char, int, 1>(0) == char8_32); static_assert(to_alloc<char16_t, int, 1>(0) == char16_32); static_assert(to_alloc<char32_t, int, 1>(0) == char32_32); static_assert(to_alloc<long, int, 1>(0) == char64_32); static_assert(to_alloc<char, int, 1>(char8_32) == char8_32 + 1); static_assert(to_alloc<char16_t, int, 1>(char16_32) == char16_32 + 1); static_assert(to_alloc<char32_t, int, 1>(char32_32) == char32_32 + 1); static_assert(to_alloc<long, int, 1>(char64_32) == char64_32 + 1); static_assert(to_alloc<char, int, 1>(char8_32 + 1) == 16); static_assert(to_alloc<char16_t, int, 1>(char16_32 + 1) == 8); static_assert(to_alloc<char32_t, int, 1>(char32_32 + 1) == 4); static_assert(to_alloc<long, int, 1>(char64_32 + 1) == 4); // 32 bit system, __endian_factor = 2 static_assert(to_alloc<char, int, 2>(0) == char8_32); static_assert(to_alloc<char16_t, int, 2>(0) == char16_32); static_assert(to_alloc<char32_t, int, 2>(0) == char32_32); static_assert(to_alloc<long, int, 2>(0) == char64_32); static_assert(to_alloc<char, int, 2>(char8_32) == char8_32 + 1); static_assert(to_alloc<char16_t, int, 2>(char16_32) == char16_32 + 1); static_assert(to_alloc<char32_t, int, 2>(char32_32) == 4); static_assert(to_alloc<long, int, 2>(char64_32) == 4); static_assert(to_alloc<char, int, 2>(char8_32 + 1) == 16); static_assert(to_alloc<char16_t, int, 2>(char16_32 + 1) == 8); static_assert(to_alloc<char32_t, int, 2>(char32_32 + 1) == 4); static_assert(to_alloc<long, int, 2>(char64_32 + 1) == 4); } ```
Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write If you have received no comments on your PR for a week, you can request a review If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. |
@llvm/pr-subscribers-libcxx Author: Valery Mironov (MBkkt) Changes
https://godbolt.org/z/fGP6e11ed
Full diff: https://github.com/llvm/llvm-project/pull/92652.diff 1 Files Affected:
diff --git a/libcxx/include/string b/libcxx/include/string
index 1db803e822d72..4d66e1a6be31e 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -1984,13 +1984,8 @@ private:
}
enum { __alignment = 8 };
static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type __recommend(size_type __s) _NOEXCEPT {
- if (__s < __min_cap) {
- return static_cast<size_type>(__min_cap) - 1;
- }
const size_type __boundary = sizeof(value_type) < __alignment ? __alignment / sizeof(value_type) : __endian_factor;
size_type __guess = __align_it<__boundary>(__s + 1) - 1;
- if (__guess == __min_cap)
- __guess += __endian_factor;
return __guess;
}
@@ -3249,8 +3244,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::re
if (__requested_capacity <= capacity())
return;
- size_type __target_capacity = std::max(__requested_capacity, size());
- __target_capacity = __recommend(__target_capacity);
+ __target_capacity = __recommend(__target_capacity);
if (__target_capacity == capacity())
return;
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you expand a bit on why you think these are unnecessary? All of these checks look like they're required for correctness according to the standard or to keep string-internal invariants.
Change 1 if (__s < __min_cap) {
return static_cast<size_type>(__min_cap) - 1;
} This is unnecessary because Change 2: Already done here 4562efc if (__requested_capacity <= capacity())
return;
size_type __target_capacity = std::max(__requested_capacity, size());
__target_capacity = __recommend(__target_capacity);
Change 3 if (__guess == __min_cap)
__guess += __endian_factor; I'm not sure about best option, but current code is definitely not the best option. When string user requested 23/11/5 characters (char, char16_t, char32_t), we need only 24 bytes from allocator, without reason now we requesting 25/26 bytes (depends on endianness) This change makes it request 24 bytes in such cases. It's not required by standard because part of implementation. |
@philnik777 Hello, can you review this PR? If something not clear I can try to explain it with other words |
✅ With the latest revision this PR passed the C/C++ code formatter. |
In general I think maybe for most allocators better will be add + 1 for min_cap, because 24 size class is rare:
https://godbolt.org/z/fGP6e11ed