Skip to content

Commit 687bb98

Browse files
authored
Merge pull request #7402 from realm/tg/obj-perf
Make Obj trivial and add a separate ObjCollectionParent type
2 parents 666bb25 + 728ba67 commit 687bb98

34 files changed

+730
-659
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
### Enhancements
44
* Add support to synchronize collections embedded in Mixed properties and other collections (except sets) ([PR #7353](https://github.com/realm/realm-core/pull/7353)).
5+
* Improve performance of change notifications on nested collections somewhat ([PR #7402](https://github.com/realm/realm-core/pull/7402)).
56

67
### Fixed
78
* Fixed conflict resolution bug which may result in an crash when the AddInteger instruction on Mixed properties is merged against updates to a non-integer type ([PR #7353](https://github.com/realm/realm-core/pull/7353)).
89
* Fix a spurious crash related to opening a Realm on background thread while the process was in the middle of exiting ([#7420](https://github.com/realm/realm-core/issues/7420jj))
10+
* Fix a data race in change notification delivery when running at debug log level ([PR #7402](https://github.com/realm/realm-core/pull/7402), since v14.0.0).
11+
* Fix a 10-15% performance regression when reading data from the Realm resulting from Obj being made a non-trivial type ([PR #7402](https://github.com/realm/realm-core/pull/7402), since v14.0.0).
912

1013
### Breaking changes
1114
* Remove `realm_scheduler_set_default_factory()` and `realm_scheduler_has_default_factory()`, and change the `Scheduler` factory function to a bare function pointer rather than a `UniqueFunction` so that it does not have a non-trivial destructor.

src/realm/collection.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -237,4 +237,25 @@ void Collection::get_any(QueryCtrlBlock& ctrl, Mixed val, size_t index)
237237
}
238238
}
239239

240+
UpdateStatus CollectionBase::do_init_from_parent(BPlusTreeBase* tree, ref_type ref, bool allow_create)
241+
{
242+
if (ref) {
243+
tree->init_from_ref(ref);
244+
}
245+
else {
246+
if (tree->init_from_parent()) {
247+
// All is well
248+
return UpdateStatus::Updated;
249+
}
250+
if (!allow_create) {
251+
tree->detach();
252+
return UpdateStatus::Detached;
253+
}
254+
// The ref in the column was NULL, create the tree in place.
255+
tree->create();
256+
REALM_ASSERT(tree->is_attached());
257+
}
258+
return UpdateStatus::Updated;
259+
}
260+
240261
} // namespace realm

src/realm/collection.hpp

+30-29
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,18 @@ class DummyParent : public CollectionParent {
5353
{
5454
return m_obj;
5555
}
56+
uint32_t parent_version() const noexcept final
57+
{
58+
return 0;
59+
}
5660

5761
protected:
5862
Obj m_obj;
5963
ref_type m_ref;
60-
UpdateStatus update_if_needed_with_status() const final
64+
UpdateStatus update_if_needed() const final
6165
{
6266
return UpdateStatus::Updated;
6367
}
64-
bool update_if_needed() const final
65-
{
66-
return true;
67-
}
6868
ref_type get_collection_ref(Index, CollectionType) const final
6969
{
7070
return m_ref;
@@ -255,6 +255,7 @@ class CollectionBase : public Collection {
255255
CollectionBase& operator=(CollectionBase&&) noexcept = default;
256256

257257
void validate_index(const char* msg, size_t index, size_t size) const;
258+
static UpdateStatus do_init_from_parent(BPlusTreeBase* tree, ref_type ref, bool allow_create);
258259
};
259260

260261
inline std::string_view collection_type_name(CollectionType col_type, bool uppercase = false)
@@ -492,7 +493,7 @@ class CollectionBaseImpl : public Interface, protected ArrayParent {
492493
if (m_parent) {
493494
try {
494495
// Update the parent. Will throw if parent is not existing.
495-
switch (m_parent->update_if_needed_with_status()) {
496+
switch (m_parent->update_if_needed()) {
496497
case UpdateStatus::Updated:
497498
// Make sure to update next time around
498499
m_content_version = 0;
@@ -524,7 +525,7 @@ class CollectionBaseImpl : public Interface, protected ArrayParent {
524525
{
525526
try {
526527
// `has_changed()` sneakily modifies internal state.
527-
update_if_needed_with_status();
528+
update_if_needed();
528529
if (m_last_content_version != m_content_version) {
529530
m_last_content_version = m_content_version;
530531
return true;
@@ -563,24 +564,27 @@ class CollectionBaseImpl : public Interface, protected ArrayParent {
563564
m_content_version = 0;
564565
}
565566

567+
CollectionParent* get_owner() const noexcept
568+
{
569+
return m_parent;
570+
}
571+
566572
void to_json(std::ostream&, JSONOutputMode, util::FunctionRef<void(const Mixed&)>) const override;
567573

568574
using Interface::get_owner_key;
569575
using Interface::get_table;
570576
using Interface::get_target_table;
571577

572578
protected:
573-
Obj m_obj_mem;
579+
ObjCollectionParent m_obj_mem;
574580
std::shared_ptr<CollectionParent> m_col_parent;
575581
CollectionParent::Index m_index;
576-
mutable size_t m_my_version = 0;
577582
ColKey m_col_key;
578-
bool m_nullable = false;
579-
580583
mutable uint_fast64_t m_content_version = 0;
581-
582584
// Content version used by `has_changed()`.
583585
mutable uint_fast64_t m_last_content_version = 0;
586+
mutable uint32_t m_parent_version = 0;
587+
bool m_nullable = false;
584588

585589
CollectionBaseImpl() = default;
586590
CollectionBaseImpl(const CollectionBaseImpl& other)
@@ -650,13 +654,14 @@ class CollectionBaseImpl : public Interface, protected ArrayParent {
650654

651655
UpdateStatus get_update_status() const
652656
{
653-
UpdateStatus status = m_parent ? m_parent->update_if_needed_with_status() : UpdateStatus::Detached;
657+
UpdateStatus status = m_parent ? m_parent->update_if_needed() : UpdateStatus::Detached;
654658

655659
if (status != UpdateStatus::Detached) {
656660
auto content_version = m_alloc->get_content_version();
657-
if (content_version != m_content_version || m_my_version != m_parent->m_parent_version) {
661+
auto parent_version = m_parent->parent_version();
662+
if (content_version != m_content_version || m_parent_version != parent_version) {
658663
m_content_version = content_version;
659-
m_my_version = m_parent->m_parent_version;
664+
m_parent_version = parent_version;
660665
status = UpdateStatus::Updated;
661666
}
662667
}
@@ -667,18 +672,14 @@ class CollectionBaseImpl : public Interface, protected ArrayParent {
667672
/// Refresh the parent object (if needed) and compare version numbers.
668673
/// Return true if the collection should initialize from parent
669674
/// Throws if the owning object no longer exists.
670-
bool should_update()
675+
bool should_update() const
671676
{
672677
check_parent();
673-
bool changed = m_parent->update_if_needed(); // Throws if the object does not exist.
674-
auto content_version = m_alloc->get_content_version();
675-
676-
if (changed || content_version != m_content_version || m_my_version != m_parent->m_parent_version) {
677-
m_content_version = content_version;
678-
m_my_version = m_parent->m_parent_version;
679-
return true;
678+
auto status = get_update_status();
679+
if (status == UpdateStatus::Detached) {
680+
throw StaleAccessor("Parent no longer exists");
680681
}
681-
return false;
682+
return status == UpdateStatus::Updated;
682683
}
683684

684685
void bump_content_version()
@@ -728,19 +729,19 @@ class CollectionBaseImpl : public Interface, protected ArrayParent {
728729
void set_backlink(ColKey col_key, ObjLink new_link) const
729730
{
730731
check_parent();
731-
m_parent->set_backlink(col_key, new_link);
732+
m_parent->get_object().set_backlink(col_key, new_link);
732733
}
733734
// Used when replacing a link, return true if CascadeState contains objects to remove
734735
bool replace_backlink(ColKey col_key, ObjLink old_link, ObjLink new_link, CascadeState& state) const
735736
{
736737
check_parent();
737-
return m_parent->replace_backlink(col_key, old_link, new_link, state);
738+
return m_parent->get_object().replace_backlink(col_key, old_link, new_link, state);
738739
}
739740
// Used when removing a backlink, return true if CascadeState contains objects to remove
740741
bool remove_backlink(ColKey col_key, ObjLink old_link, CascadeState& state) const
741742
{
742743
check_parent();
743-
return m_parent->remove_backlink(col_key, old_link, state);
744+
return m_parent->get_object().remove_backlink(col_key, old_link, state);
744745
}
745746

746747
/// Reset the accessor's tracking of the content version. Derived classes
@@ -796,7 +797,7 @@ class CollectionBaseImpl : public Interface, protected ArrayParent {
796797
///
797798
/// If no change has happened to the data, this function returns
798799
/// `UpdateStatus::NoChange`, and the caller is allowed to not do anything.
799-
virtual UpdateStatus update_if_needed_with_status() const = 0;
800+
virtual UpdateStatus update_if_needed() const = 0;
800801
};
801802

802803
namespace _impl {
@@ -884,7 +885,7 @@ class ObjCollectionBase : public Interface, public _impl::ObjListProxy {
884885
/// `BPlusTree<T>`.
885886
virtual BPlusTree<ObjKey>* get_mutable_tree() const = 0;
886887

887-
/// Implements update_if_needed() in a way that ensures the consistency of
888+
/// Implements `update_if_needed()` in a way that ensures the consistency of
888889
/// the unresolved list. Derived classes should call this instead of calling
889890
/// `update_if_needed()` on their inner accessor.
890891
UpdateStatus update_if_needed() const

0 commit comments

Comments
 (0)