-
Type:
Improvement
-
Resolution: Unresolved
-
Priority:
Major - P3
-
None
-
Affects Version/s: None
-
Component/s: None
-
3 - M (<= 1 month)
-
2713
Description
I'm completely aware this goes against the default zero-copy architecture of Realm, but for the sake of performance critical applications, I do hope this gathers attention.
By default, the properties of a RealmObject are direct pointers to its corresponding data on the disk and do not use any backing fields for caching. This is a boost for the memory because not all the properties of a RealmObject are used for every query anyway. But what if memory is not an issue and certain properties of a certain type inheriting from RealmObject/EmbedddedObject are frequently accessed by the program and what if the program has to individually access hundreds or thousands of these objects?
My proposal is to provide an attribute (e.g. [Memoized]) that will tell Fody to add a backing field for the property and update it whenever the object changes. Below is a simplified example:
// Sample RealmObject public class Item : RealmObject { ... public int OrdinaryRealmProperty { get; set; } [Memoized] public int MemoizedRealmProperty { get; set; } ... } // Sample RealmObject after weaving public class Item : RealmObject { ... public int OrdinaryRealmProperty { get => ... // default getter method weaved by fody set => ... // default setter method weaved by fody } private int _memoizedRealmProperty; public int MemoizedRealmProperty { get => _memoizedRealmProperty; set => ... // default setter method weaved by fody } protected override void OnPropertyChanged( string propertyName ) { base.OnPropertyChanged( string propertyName ); _memoizedRealmProperty = ... // default getter method weaved by fody } ... }
I strongly believe the zero-copy should still be the default behavior of a property, but I'm looking forward for a similar feature to be implemented for properties that are frequently accessed.
Workaround
One can just simply implement the boilerplate above. Define two properties: one for the persisted property, one for the property that uses a backing field, then override the OnPropertyChanged to always refresh the backing field when the persisted property changes.
For my current use case however, I'm currently trying to attach a set of properties to ALL my RealmObject}}s so implementing this boilerplate quickly became a mess. Defining an abstract class that inherits {{RealmObject isn't supported by the weaver either. I can also just use an EmbeddedObject and attach it to all my RealmObject}}s but I still have to pay the performance penalty of get accessor for the {{EmbeddedObject property.
The workaround I went with is to just store these values in a Dictionary<TKey, TValue> with the hash code of the RealmObject as keys. Unfortunately, benchmarking showed that the speed gained wasn't large enough (~30-50 nanoseconds) compared to the boilerplate workaround aforementioned (direct access to a backing field, should be almost indistinguishable from an empty method) given how fast already is the woven get accessor for a RealmObject (~150 nanoseconds).
How important is this improvement for you?
Actually, if feasible, I'd even prefer having the option to completely memoize all properties of objects by default in my current case (using Realm for an internal blazor server application) where speed in fetching data is much more of a concern than memory.