From ade8cb41198287146f68ec4e8a21e159a8024118 Mon Sep 17 00:00:00 2001 From: James Stone Date: Wed, 22 Jun 2022 10:58:20 -0700 Subject: [PATCH 1/2] optimize memory allocation of changesets with many small strings --- CHANGELOG.md | 1 + src/realm/sync/changeset.hpp | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d7e70ce9e0..4a922ddd39c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Fixed * ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?) * Fix a UBSan failure when mapping encrypted pages. +* Improved performance of sync clients during integration of changesets with many small strings (totalling > 1024 bytes per changeset) on iOS 14, and devices which have restrictive or fragmented memory. ([#5614](https://github.com/realm/realm-core/issues/5614)) ### Breaking changes * None. diff --git a/src/realm/sync/changeset.hpp b/src/realm/sync/changeset.hpp index ceed732dcf9..1e8dd933dec 100644 --- a/src/realm/sync/changeset.hpp +++ b/src/realm/sync/changeset.hpp @@ -539,7 +539,13 @@ inline StringData Changeset::string_data() const noexcept inline StringBufferRange Changeset::append_string(StringData string) { - m_string_buffer->reserve(1024); // we expect more strings + // small string optimization; we expect more strings, but constantly requesting many small allocation increases + // to the string buffer can become a performance bottleneck in highly fragmented or low memory devices + constexpr size_t small_string_buffer_size = 1024; + if (m_string_buffer->capacity() - m_string_buffer->size() < string.size() && + string.size() < small_string_buffer_size) { + m_string_buffer->reserve(m_string_buffer->capacity() + small_string_buffer_size); + } size_t offset = m_string_buffer->size(); m_string_buffer->append(string.data(), string.size()); return StringBufferRange{uint32_t(offset), uint32_t(string.size())}; From bfc9de9c86a2ce15590512de2c07a63fc247977a Mon Sep 17 00:00:00 2001 From: James Stone Date: Thu, 23 Jun 2022 15:50:43 -0700 Subject: [PATCH 2/2] let strings manage their own capacity past the first 1024 --- src/realm/sync/changeset.hpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/realm/sync/changeset.hpp b/src/realm/sync/changeset.hpp index 1e8dd933dec..4d47b1690da 100644 --- a/src/realm/sync/changeset.hpp +++ b/src/realm/sync/changeset.hpp @@ -539,12 +539,11 @@ inline StringData Changeset::string_data() const noexcept inline StringBufferRange Changeset::append_string(StringData string) { - // small string optimization; we expect more strings, but constantly requesting many small allocation increases - // to the string buffer can become a performance bottleneck in highly fragmented or low memory devices + // We expect more strings. Only do this at the beginning because until C++20, reserve + // will shrink_to_fit if the request is less than the current capacity. constexpr size_t small_string_buffer_size = 1024; - if (m_string_buffer->capacity() - m_string_buffer->size() < string.size() && - string.size() < small_string_buffer_size) { - m_string_buffer->reserve(m_string_buffer->capacity() + small_string_buffer_size); + if (m_string_buffer->capacity() < small_string_buffer_size) { + m_string_buffer->reserve(small_string_buffer_size); } size_t offset = m_string_buffer->size(); m_string_buffer->append(string.data(), string.size());