forked from qt-creator/qt-creator
Utils: Optimze SmallString copies for larger sizes
So far the whole class was always copied. For a large SmallString classes this were often many zeros if it was empty. Now only the non zero area is copied in that case. Change-Id: Ic0ea2d6f763513858a993bba3e829c9fc947e1a6 Reviewed-by: Tim Jenssen <tim.jenssen@qt.io> Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
@@ -28,6 +28,7 @@
|
|||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
@@ -95,8 +96,8 @@ private:
|
|||||||
ControlType m_isReference : 1;
|
ControlType m_isReference : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <uint MaximumShortStringDataAreaSize>
|
template<uint MaximumShortStringDataAreaSize>
|
||||||
struct AllocatedLayout
|
struct alignas(16) AllocatedLayout
|
||||||
{
|
{
|
||||||
struct Data
|
struct Data
|
||||||
{
|
{
|
||||||
@@ -109,8 +110,8 @@ struct AllocatedLayout
|
|||||||
Data data;
|
Data data;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <uint MaximumShortStringDataAreaSize>
|
template<uint MaximumShortStringDataAreaSize>
|
||||||
struct ReferenceLayout
|
struct alignas(16) ReferenceLayout
|
||||||
{
|
{
|
||||||
constexpr ReferenceLayout() noexcept = default;
|
constexpr ReferenceLayout() noexcept = default;
|
||||||
constexpr ReferenceLayout(const char *stringPointer,
|
constexpr ReferenceLayout(const char *stringPointer,
|
||||||
@@ -131,10 +132,9 @@ struct ReferenceLayout
|
|||||||
Data data;
|
Data data;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <uint MaximumShortStringDataAreaSize>
|
template<uint MaximumShortStringDataAreaSize>
|
||||||
struct ShortStringLayout
|
struct alignas(16) ShortStringLayout
|
||||||
{
|
{
|
||||||
constexpr ShortStringLayout() noexcept = default;
|
|
||||||
constexpr ShortStringLayout(
|
constexpr ShortStringLayout(
|
||||||
typename ControlBlock<MaximumShortStringDataAreaSize>::SizeType shortStringSize) noexcept
|
typename ControlBlock<MaximumShortStringDataAreaSize>::SizeType shortStringSize) noexcept
|
||||||
: control(shortStringSize, false, false)
|
: control(shortStringSize, false, false)
|
||||||
@@ -144,21 +144,18 @@ struct ShortStringLayout
|
|||||||
char string[MaximumShortStringDataAreaSize];
|
char string[MaximumShortStringDataAreaSize];
|
||||||
};
|
};
|
||||||
|
|
||||||
template <uint MaximumShortStringDataAreaSize>
|
template<uint MaximumShortStringDataAreaSize, typename = void>
|
||||||
struct StringDataLayout {
|
struct StringDataLayout
|
||||||
|
{
|
||||||
static_assert(MaximumShortStringDataAreaSize >= 15, "Size must be greater equal than 15 bytes!");
|
static_assert(MaximumShortStringDataAreaSize >= 15, "Size must be greater equal than 15 bytes!");
|
||||||
static_assert(MaximumShortStringDataAreaSize < 64
|
static_assert(MaximumShortStringDataAreaSize < 32, "Size must be less than 32 bytes!");
|
||||||
? ((MaximumShortStringDataAreaSize + 1) % 16) == 0
|
static_assert(((MaximumShortStringDataAreaSize + 1) % 16) == 0,
|
||||||
: ((MaximumShortStringDataAreaSize + 2) % 16) == 0,
|
"Size + 1 must be dividable by 16 if under 64 and Size + 2 must be dividable by "
|
||||||
"Size + 1 must be dividable by 16 if under 64 and Size + 2 must be dividable by 16 if over 64!");
|
"16 if over 64!");
|
||||||
|
|
||||||
StringDataLayout() noexcept
|
StringDataLayout() noexcept { reset(); }
|
||||||
{
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr StringDataLayout(const char *string,
|
constexpr StringDataLayout(const char *string, size_type size) noexcept
|
||||||
size_type size) noexcept
|
|
||||||
: reference(string, size, 0)
|
: reference(string, size, 0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@@ -174,13 +171,73 @@ struct StringDataLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr static
|
constexpr static size_type shortStringCapacity() noexcept
|
||||||
size_type shortStringCapacity() noexcept
|
|
||||||
{
|
{
|
||||||
return MaximumShortStringDataAreaSize - 1;
|
return MaximumShortStringDataAreaSize - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset()
|
constexpr void reset()
|
||||||
|
{
|
||||||
|
shortString.control = ControlBlock<MaximumShortStringDataAreaSize>();
|
||||||
|
shortString.string[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
union {
|
||||||
|
AllocatedLayout<MaximumShortStringDataAreaSize> allocated;
|
||||||
|
ReferenceLayout<MaximumShortStringDataAreaSize> reference;
|
||||||
|
ShortStringLayout<MaximumShortStringDataAreaSize> shortString;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template<uint MaximumShortStringDataAreaSize>
|
||||||
|
struct StringDataLayout<MaximumShortStringDataAreaSize,
|
||||||
|
std::enable_if_t<MaximumShortStringDataAreaSize >= 32>>
|
||||||
|
{
|
||||||
|
static_assert(MaximumShortStringDataAreaSize > 31, "Size must be greater than 31 bytes!");
|
||||||
|
static_assert(MaximumShortStringDataAreaSize < 64
|
||||||
|
? ((MaximumShortStringDataAreaSize + 1) % 16) == 0
|
||||||
|
: ((MaximumShortStringDataAreaSize + 2) % 16) == 0,
|
||||||
|
"Size + 1 must be dividable by 16 if under 64 and Size + 2 must be dividable by "
|
||||||
|
"16 if over 64!");
|
||||||
|
|
||||||
|
StringDataLayout() noexcept { reset(); }
|
||||||
|
|
||||||
|
constexpr StringDataLayout(const char *string, size_type size) noexcept
|
||||||
|
: reference(string, size, 0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template<size_type Size>
|
||||||
|
constexpr StringDataLayout(const char (&string)[Size]) noexcept
|
||||||
|
{
|
||||||
|
if constexpr (Size <= MaximumShortStringDataAreaSize) {
|
||||||
|
shortString = {Size - 1};
|
||||||
|
for (size_type i = 0; i < Size; ++i)
|
||||||
|
shortString.string[i] = string[i];
|
||||||
|
} else {
|
||||||
|
reference = {string, Size - 1, 0};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StringDataLayout(const StringDataLayout &other) noexcept { *this = other; }
|
||||||
|
|
||||||
|
StringDataLayout &operator=(const StringDataLayout &other) noexcept
|
||||||
|
{
|
||||||
|
constexpr auto controlBlockSize = sizeof(ControlBlock<MaximumShortStringDataAreaSize>);
|
||||||
|
auto shortStringLayoutSize = other.shortString.control.stringSize() + controlBlockSize;
|
||||||
|
constexpr auto referenceLayoutSize = sizeof(ReferenceLayout<MaximumShortStringDataAreaSize>);
|
||||||
|
std::memcpy(&shortString,
|
||||||
|
&other.shortString,
|
||||||
|
std::max(shortStringLayoutSize, referenceLayoutSize));
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr static size_type shortStringCapacity() noexcept
|
||||||
|
{
|
||||||
|
return MaximumShortStringDataAreaSize - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void reset()
|
||||||
{
|
{
|
||||||
shortString.control = ControlBlock<MaximumShortStringDataAreaSize>();
|
shortString.control = ControlBlock<MaximumShortStringDataAreaSize>();
|
||||||
shortString.string[0] = '\0';
|
shortString.string[0] = '\0';
|
||||||
|
@@ -38,6 +38,14 @@ using Utils::SmallString;
|
|||||||
using Utils::SmallStringLiteral;
|
using Utils::SmallStringLiteral;
|
||||||
using Utils::SmallStringView;
|
using Utils::SmallStringView;
|
||||||
|
|
||||||
|
static_assert(32 == sizeof(Utils::BasicSmallString<31>));
|
||||||
|
static_assert(64 == sizeof(Utils::BasicSmallString<63>));
|
||||||
|
static_assert(192 == sizeof(Utils::BasicSmallString<190>));
|
||||||
|
|
||||||
|
static_assert(16 == alignof(Utils::BasicSmallString<31>));
|
||||||
|
static_assert(16 == alignof(Utils::BasicSmallString<63>));
|
||||||
|
static_assert(16 == alignof(Utils::BasicSmallString<190>));
|
||||||
|
|
||||||
TEST(SmallString, BasicStringEqual)
|
TEST(SmallString, BasicStringEqual)
|
||||||
{
|
{
|
||||||
ASSERT_THAT(SmallString("text"), Eq(SmallString("text")));
|
ASSERT_THAT(SmallString("text"), Eq(SmallString("text")));
|
||||||
@@ -1524,6 +1532,80 @@ TEST(SmallString, LongSmallStringMoveConstuctor)
|
|||||||
ASSERT_THAT(copy, SmallString("this is a very very very very long text"));
|
ASSERT_THAT(copy, SmallString("this is a very very very very long text"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(SmallString, ShortPathStringMoveConstuctor)
|
||||||
|
{
|
||||||
|
PathString text("text");
|
||||||
|
|
||||||
|
auto copy = std::move(text);
|
||||||
|
|
||||||
|
ASSERT_TRUE(text.isEmpty());
|
||||||
|
ASSERT_THAT(copy, SmallString("text"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SmallString, LongPathStringMoveConstuctor)
|
||||||
|
{
|
||||||
|
PathString text(
|
||||||
|
"this is a very very very very very very very very very very very very very very very very "
|
||||||
|
"very very very very very very very very very very very very very very very very very very "
|
||||||
|
"very very very very very very very very very very very very very very long text");
|
||||||
|
|
||||||
|
auto copy = std::move(text);
|
||||||
|
|
||||||
|
ASSERT_TRUE(text.isEmpty());
|
||||||
|
ASSERT_THAT(
|
||||||
|
copy,
|
||||||
|
PathString(
|
||||||
|
"this is a very very very very very very very very very very very very very very very "
|
||||||
|
"very very very very very very very very very very very very very very very very very "
|
||||||
|
"very very very very very very very very very very very very very very very very long "
|
||||||
|
"text"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SmallString, ShortSmallStringMoveConstuctorToSelf)
|
||||||
|
{
|
||||||
|
SmallString text("text");
|
||||||
|
|
||||||
|
text = std::move(text);
|
||||||
|
|
||||||
|
ASSERT_THAT(text, SmallString("text"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SmallString, LongSmallStringMoveConstuctorToSelf)
|
||||||
|
{
|
||||||
|
SmallString text("this is a very very very very long text");
|
||||||
|
|
||||||
|
text = std::move(text);
|
||||||
|
|
||||||
|
ASSERT_THAT(text, SmallString("this is a very very very very long text"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SmallString, ShortPathStringMoveConstuctorToSelf)
|
||||||
|
{
|
||||||
|
PathString text("text");
|
||||||
|
|
||||||
|
text = std::move(text);
|
||||||
|
|
||||||
|
ASSERT_THAT(text, SmallString("text"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SmallString, LongPathStringMoveConstuctorToSelf)
|
||||||
|
{
|
||||||
|
PathString text(
|
||||||
|
"this is a very very very very very very very very very very very very very very very very "
|
||||||
|
"very very very very very very very very very very very very very very very very very very "
|
||||||
|
"very very very very very very very very very very very very very very long text");
|
||||||
|
|
||||||
|
text = std::move(text);
|
||||||
|
|
||||||
|
ASSERT_THAT(
|
||||||
|
text,
|
||||||
|
PathString(
|
||||||
|
"this is a very very very very very very very very very very very very very very very "
|
||||||
|
"very very very very very very very very very very very very very very very very very "
|
||||||
|
"very very very very very very very very very very very very very very very very long "
|
||||||
|
"text"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(SmallString, ShortSmallStringCopyAssignment)
|
TEST(SmallString, ShortSmallStringCopyAssignment)
|
||||||
{
|
{
|
||||||
SmallString text("text");
|
SmallString text("text");
|
||||||
|
Reference in New Issue
Block a user