Utils: SmallString trades copies for branches

There can be quite some copies with the swap method for the default case
that the string is inside the small string area. Instead now we minimize
copies of the smallstring area at the expense of more branches.

The swap approch needs no self tests and it uses the destructor to
remove allocated resources but it can copy three times more often. And
the destructor still has an additional branch in the copy case. Only in
the move case we can use the destructor of the other instance. This can
be surprising for some users which expect that the instance is empty
after a move(actually assuming that a instance is empty after a move is
undefined behavior).

Because SmallString is designed to hold the string in almost all cases
inside the small string area that case should be optimized.

Change-Id: I22cb62aa99b9713a221f103971f149c05e9ff6fa
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Marco Bubke
2022-07-31 01:09:08 +02:00
parent fa32b652c8
commit 735346adb5
2 changed files with 49 additions and 19 deletions

View File

@@ -170,19 +170,25 @@ public:
Memory::deallocate(m_data.allocated.data.pointer); Memory::deallocate(m_data.allocated.data.pointer);
} }
BasicSmallString(const BasicSmallString &string) BasicSmallString(const BasicSmallString &other)
{ {
if (string.isShortString() || string.isReadOnlyReference()) if (Q_LIKELY(other.isShortString() || other.isReadOnlyReference()))
m_data = string.m_data; m_data = other.m_data;
else else
new (this) BasicSmallString{string.data(), string.size()}; new (this) BasicSmallString{other.data(), other.size()};
} }
BasicSmallString &operator=(const BasicSmallString &other) BasicSmallString &operator=(const BasicSmallString &other)
{ {
BasicSmallString copy = other; if (Q_LIKELY(this != &other)) {
if (Q_UNLIKELY(hasAllocatedMemory()))
Memory::deallocate(m_data.allocated.data.pointer);
swap(*this, copy); if (Q_LIKELY(other.isShortString() || other.isReadOnlyReference()))
m_data = other.m_data;
else
new (this) BasicSmallString{other.data(), other.size()};
}
return *this; return *this;
} }
@@ -195,17 +201,18 @@ public:
BasicSmallString &operator=(BasicSmallString &&other) noexcept BasicSmallString &operator=(BasicSmallString &&other) noexcept
{ {
swap(*this, other); if (Q_LIKELY(this != &other)) {
if (Q_UNLIKELY(hasAllocatedMemory()))
Memory::deallocate(m_data.allocated.data.pointer);
m_data = std::move(other.m_data);
other.m_data.reset();
}
return *this; return *this;
} }
BasicSmallString take() noexcept BasicSmallString take() noexcept { return std::move(*this); }
{
auto tmp = std::move(*this);
return tmp;
}
BasicSmallString clone() const BasicSmallString clone() const
{ {
BasicSmallString clonedString(m_data); BasicSmallString clonedString(m_data);

View File

@@ -1518,7 +1518,6 @@ TEST(SmallString, ShortSmallStringMoveConstuctor)
auto copy = std::move(text); auto copy = std::move(text);
ASSERT_TRUE(text.isEmpty());
ASSERT_THAT(copy, SmallString("text")); ASSERT_THAT(copy, SmallString("text"));
} }
@@ -1528,7 +1527,6 @@ TEST(SmallString, LongSmallStringMoveConstuctor)
auto copy = std::move(text); auto copy = std::move(text);
ASSERT_TRUE(text.isEmpty());
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"));
} }
@@ -1538,7 +1536,6 @@ TEST(SmallString, ShortPathStringMoveConstuctor)
auto copy = std::move(text); auto copy = std::move(text);
ASSERT_TRUE(text.isEmpty());
ASSERT_THAT(copy, SmallString("text")); ASSERT_THAT(copy, SmallString("text"));
} }
@@ -1551,7 +1548,6 @@ TEST(SmallString, LongPathStringMoveConstuctor)
auto copy = std::move(text); auto copy = std::move(text);
ASSERT_TRUE(text.isEmpty());
ASSERT_THAT( ASSERT_THAT(
copy, copy,
PathString( PathString(
@@ -1642,7 +1638,6 @@ TEST(SmallString, ShortSmallStringMoveAssignment)
copy = std::move(text); copy = std::move(text);
ASSERT_THAT(text, SmallString("more text"));
ASSERT_THAT(copy, SmallString("text")); ASSERT_THAT(copy, SmallString("text"));
} }
@@ -1653,10 +1648,38 @@ TEST(SmallString, LongSmallStringMoveAssignment)
copy = std::move(text); copy = std::move(text);
ASSERT_THAT(text, SmallString("more text"));
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, ShortPathStringMoveAssignment)
{
PathString text("text");
PathString copy("more text");
copy = std::move(text);
ASSERT_THAT(copy, SmallString("text"));
}
TEST(SmallString, LongPathStringMoveAssignment)
{
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");
PathString copy("more text");
copy = std::move(text);
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, ShortSmallStringTake) TEST(SmallString, ShortSmallStringTake)
{ {
SmallString text("text"); SmallString text("text");