Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speed-up calls to Realm.Manage #857

Merged
merged 6 commits into from
Oct 7, 2016
Merged

Speed-up calls to Realm.Manage #857

merged 6 commits into from
Oct 7, 2016

Conversation

nirinchev
Copy link
Member

@nirinchev nirinchev commented Oct 6, 2016

This is a change that aims two things

  1. Bring the performance of Realm.Manage closer to Realm.CreateObject
  2. Pave the way for AddOrUpdate (Update object by PrimaryKey #808)

The essence here is weaving CopyToRealm in IRealmObjectHelper implementors that replaces the reflection that we used before. Here's the performance comparison

CreateObject Manage (woven) Manage (reflection)
Sparse 1235 2931 1523 11291
Dense 827 951 2527

Times are in ms and the test was performed with 100000 objects.
Sparse refers to objects with a lot of properties, where only a few are set.
Dense refers to objects where all properties are set.

The current implementation guards against hitting ObjectStore by doing

if (field != default(fieldType))
{
    Property = field
}

only for value types, objects, and lists. It doesn't support nullable value types because the IL is getting out of hand as is and didn't seem like it would add a lot of value.

Copy link
Member

@fealebenpae fealebenpae left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks very good, aside from a couple of things noted.

It seems to me that it would be very cheap for us to always compare scalar fields against their default value, and it shouldn't be much effort to add this check. Can you extend this PR with that?

@@ -25,6 +27,34 @@ internal class DynamicRealmObjectHelper : IRealmObjectHelper
{
internal static readonly DynamicRealmObjectHelper Instance = new DynamicRealmObjectHelper();

public void CopyToRealm(RealmObject instance)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Objects whose helper is DynamicRealmObjectHelper cannot exist in an unmanaged state - they always belong to a realm. I think it's safe to make this method throw an exception instead.

*foreach* list woven property in casted's schema
var list = casted.field;
casted.field = null;
((ICopyValuesFrom)casted.Property).CopyValuesFrom(list);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should get rid of ICopyValuesFrom instead of making it public. Let's add a public AddRange(IEnumerable<T>) to RealmList<T> and call that instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That will clash with our desire to make RealmList<T> internal (#853), so which one do we want more?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want both :-) Do you think it will be too much for this PR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, you're right. How about we weave the foreach cycling in CopyToRealm which would call IList<T>.Add(T)?

Copy link
Member Author

@nirinchev nirinchev Oct 6, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was a bit of a pain, but I got rid of ICopyValuesFrom 😤 I'll leave internalizing RealmList to #858.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice!!

@nirinchev
Copy link
Member Author

I figured it's better to split it in two, so that we can brag about "major speed improvements to Manage" in our release notes. Then after a release or two, we could add the default check and brag about speed improvements again 😆 If we make it ultra-fast in one go, people will think we're not improving anymore!

@fealebenpae
Copy link
Member

Oh haha, fair point. We can save some performance improvements for the next release.

@@ -85,5 +82,90 @@ public void RealmObject_WhenStandalone_ShouldHaveDefaultEqualsImplementation()

Assert.DoesNotThrow(() => _person.Equals(otherPerson));
}

[Test]
public void RealmObject_WhenManaged_ShouldNotThrow()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is fine but I would also like a test in WeaverTests as well that verifies that the expected get and set methods are invoked.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I will take a look at the WeaverTests infrastructures and try to come up with one.

*foreach* list woven property in casted's schema
var list = casted.field;
casted.field = null;
((ICopyValuesFrom)casted.Property).CopyValuesFrom(list);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want both :-) Do you think it will be too much for this PR?

@nirinchev
Copy link
Member Author

nirinchev commented Oct 6, 2016

Ping @fealebenpae, @kristiandupont care to take a second look?

@@ -810,6 +809,83 @@ private static bool IsRealmList(PropertyDefinition property)
return property.PropertyType.Name == "RealmList`1" && property.PropertyType.Namespace == "Realms";
}

private static bool IsDateTimeOffset(PropertyDefinition property)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know we've been using strings already, but would it be nicer to create something like

private static bool Is<T>(PropertyDefinition property)
{
    return property.PropertyType.Name == typeof(T).Name && ...
}

?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it would, but I didn't want to diverge too much from the coding style. Also, when we add StyleCop, we'll probably have to reorder/reformat the whole file and it'll complain about constants/nameof/typeof :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It bugs me that we do string comparison in the first place - I'd much rather we compare TypeDefinition instances.

@fealebenpae
Copy link
Member

I believe that you should remove the BackingFieldName property of WovenPropertyAttribute - it shouldn't be used anymore.

@nirinchev
Copy link
Member Author

nirinchev commented Oct 7, 2016

Do we need the WovenPropertyAttribute in that case? It seems we do need it after all. I'll get rid of just the backing field then.

@fealebenpae
Copy link
Member

We could get rid of WovenPropertyAttribute, but right now it's a very useful marker for properties that are persisted. Prior to that we had duplicate code in the library and the weaver.

👍 from me.

@kristiandupont
Copy link
Contributor

👍 from me as well. Very nice work!

@nirinchev nirinchev merged commit 6eee401 into master Oct 7, 2016
@nirinchev nirinchev deleted the ni/manage-speedup branch October 7, 2016 18:38
@nirinchev nirinchev removed the S:Review label Oct 7, 2016
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants