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

refactor: memoize some methods #130

Merged
merged 2 commits into from
Sep 20, 2019
Merged

refactor: memoize some methods #130

merged 2 commits into from
Sep 20, 2019

Conversation

makepanic
Copy link
Contributor

implements parts of #128

@makepanic
Copy link
Contributor Author

makepanic commented Sep 12, 2019

I've added memoization to the association classes. It might be unsafe but our tests and mirage tests work fine with it.

The issue with these methods is that they don't get an argument.
Alternatively we could extract private methods which receive the instance fields as arguments to make them safe to memoize.

@makepanic makepanic marked this pull request as ready for review September 12, 2019 06:57
@makepanic
Copy link
Contributor Author

I've also ran a slightly larger test, which uses more mirage instances to check how it compares:

  • old mirage

1416ms
1290ms
1127ms
1185ms
1172ms
1143ms
1145ms

  • memoized mirage

1053ms
969ms
938ms
909ms
973ms
969ms
918ms

Seems like a constant improvement.

@samselikoff
Copy link
Contributor

Great work 👍 I'm willing to change these things and if they cause bugs it means we need better test coverage. We should be able to push changes to Mirage if the tests pass.

Stylistically when I've done memoization in the past I've done it like this

get identifier() {
  if (!this._identifier) {
    this._identifier = `${camelize(this._container.inflector.singularize(this.key))}Ids`;
  }
  
  return this._identifier
}

Just curious if that would achieve the same effect as lodash's memoize function, I've never used it. Don't worry about changing anything yet just wanted to bring this up.

@makepanic
Copy link
Contributor Author

Just curious if that would achieve the same effect as lodash's memoize function, I've never used it. Don't worry about changing anything yet just wanted to bring this up.

It's probably better in regards to complexity.
Lodash is also checking arguments and comparing them. We can also manually cache the values but I thought it would make future maintenance more complex.

@chbonser
Copy link

I can confirm that I saw a large improvement with this change set. Models which were taking ~300ms to create are now taking ~100ms.

I will note that I did have to modify db-collection.js as follows as sometimes the id is null. I did not see this with the master branch.

@@ -344,7 +344,7 @@ class DbCollection {
     @hide
    */
   _findRecord(id) {
-    id = id.toString();
+    id = id ? id.toString() : id;

@makepanic
Copy link
Contributor Author

makepanic commented Sep 14, 2019

I will note that I did have to modify db-collection.js as follows as sometimes the id is null. I did not see this with the master branch.

That's odd.
Maybe we should memoize ourself and not let lodash do it. There might be some unexpected lodash features that play into it.

@chbonser can you maybe post a stacktrace of the _findRecord that failed on you?

I think it might be becauses some of the methods are memoizing when using this. We should extract a method to pass the this arguments in. I'll update the PR later on.

@makepanic
Copy link
Contributor Author

@chbonser I've refactored some of the association memoized methods. Now they're native getters that call the methods by passing an argument. Can you check if your adjustment is still needed?

@samselikoff I'm not sure if the latest commit is fine as it's potentially breaking api consumers due to multiple api changes.

@chbonser
Copy link

@makepanic That did the trick.

@samselikoff
Copy link
Contributor

samselikoff commented Sep 14, 2019

@makepanic Yeah I mean these methods have never been documented, there's no way every method in Mirage is public. I think they are not breaking changes but we should still clearly communicate the changes.

Great job on all this work, super appreciate of you diving into this!

So using a getter that calls a memoized function is faster eh?

@makepanic
Copy link
Contributor Author

makepanic commented Sep 14, 2019

So using a getter that calls a memoized function is faster eh?

I don't think that there's a perf difference.
It's just that a pure function is easier to memoize if its arguments are passed in. Previously it was memoized while still reading instance values. Repeated calls would've returned the initial "cached" result.

The alternative would be to keep the getX() methods and introduce _getX(argument) to allow clean memoize usages.

implements parts of #128
Copy link
Contributor

@ryanto ryanto 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 awesome, thanks so much!

I left a question and a stylistic comment. Would love to get your feedback or changes on those before merging.

lib/orm/associations/belongs-to.js Outdated Show resolved Hide resolved
lib/orm/associations/belongs-to.js Show resolved Hide resolved
implements parts of #128
@samselikoff samselikoff merged commit 3c6cba8 into miragejs:master Sep 20, 2019
@ryanto
Copy link
Contributor

ryanto commented Sep 20, 2019

🎉 🎉 🎉

Nice job @makepanic

@makepanic makepanic deleted the memoize branch September 20, 2019 06:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants