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

[breaking change] implement and default to useStrictShallowCopy, omitting getters #941

Merged
merged 8 commits into from
Apr 17, 2023

Conversation

hrsh7th
Copy link
Contributor

@hrsh7th hrsh7th commented May 18, 2022

Description

Fixes #867

This PR improves the performance when updating the object that contains a large number of entries.

Problem

The current immer takes a long time for creating new object when the draft object has large amount of keys.

From some of the investigation, I found the cause that is getOwnPropertyDescriptors (here) .

In my computer, the ownKeys and its iteration don't take a long time but getOwnPropertyDescriptors takes time near the 10ms.

I can understand that the getOwnPropertyDescriptors is needed. Because immer should copy object's descriptors such as getter/setter/non-enumerable keys.

But some of the users doesn't need that strictness, I think.

Solution

Add the ability to opt-out of strict shallow copy implementation for performance reasons.

edited

Make the default behavior not strict shallow copy. And add the ability to opt-in to strict shallow copying.

import { useStrictShallowCopy } from 'immer';

useStrictShallowCopy(true); // opt-in strict shallow copy.

Result

The following results show the benefit of this patch.

// Iteration count:
//   50
//
// Base object: 
//   Array(10000)
//   	.fill(0)
//   	.map((_, i) => [i, i])
// 
// Test case:
//   for (let i = 0; i < MAX; i++) {
//   	produce(baseState, draft => {
//   		draft[5000]++
//   	})
//   }

# large-obj - mutate large object

immer (proxy) - with setUseStrictShallowCopy: 354ms
immer (proxy) - without setUseStrictShallowCopy: 79ms
immer (es5) - with setUseStrictShallowCopy: 1228ms
immer (es5) - without setUseStrictShallowCopy: 655ms

@netlify
Copy link

netlify bot commented May 18, 2022

👷 Deploy request for quizzical-lovelace-dcbd6a pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit f9cdf8e

@hrsh7th hrsh7th force-pushed the use-strict-shallow-copy branch from b3f10f2 to d693e8f Compare May 18, 2022 07:09
@mweststrate
Copy link
Collaborator

I didn't read the code yet, but could you include a rationale in the PR description, what are you updating and why is it faster, what does the API look like, in which cases will it break things, etc?

@coveralls
Copy link

coveralls commented May 19, 2022

Pull Request Test Coverage Report for Build 2343830328

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 17 of 18 (94.44%) changed or added relevant lines in 5 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage decreased (-0.1%) to 98.433%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/core/immerClass.ts 3 4 75.0%
Totals Coverage Status
Change from base Build 2313164511: -0.1%
Covered Lines: 797
Relevant Lines: 806

💛 - Coveralls

@hrsh7th
Copy link
Contributor Author

hrsh7th commented May 19, 2022

Thank you for your advice.
I will update the PR description and try a more rational explanation.
It may take some time.

@gregdingle
Copy link

@hrsh7th @mweststrate this looks like a great PR to give users (like me) meaningfully better performance with large objects (>1000 keys).

Was it closed just for lack of activity?

@mweststrate
Copy link
Collaborator

It was missing rationale IIRC when it was closed, but now I see it is present, so reopening :) It might take some time to process though due to my own personal circumstances. Apologies upfront!

@mweststrate mweststrate reopened this Dec 16, 2022
@mweststrate mweststrate marked this pull request as ready for review December 19, 2022 08:30
@mweststrate mweststrate changed the title feat: implement useStrictShallowCopy [major] implement and default to useStrictShallowCopy, omitting getters Jan 15, 2023
@mweststrate
Copy link
Collaborator

I think it makes sense to make this behavior the new default, ship it as separate major version and make the old behavior opt-in using a flag.

@mweststrate mweststrate changed the title [major] implement and default to useStrictShallowCopy, omitting getters [breaking change] implement and default to useStrictShallowCopy, omitting getters Jan 15, 2023
@hrsh7th
Copy link
Contributor Author

hrsh7th commented Jan 15, 2023

Thank you for your review.

Nice to hear that this PR is an acceptable change.

I noticed that the test is failing. I will fix it.

@hrsh7th hrsh7th force-pushed the use-strict-shallow-copy branch from f9cdf8e to 31e61bf Compare January 27, 2023 13:27
@hrsh7th
Copy link
Contributor Author

hrsh7th commented Jan 27, 2023

Could you approve the GitHub Actions?

In my environment, this branch is passed the all tests... I want to re-try GitHub Actions.

@mweststrate
Copy link
Collaborator

done!

@hrsh7th
Copy link
Contributor Author

hrsh7th commented Jan 27, 2023

Thank you! It seems to pass the tests.
I'll do the necessary work, such as fixes that change the default behavior.

@hrsh7th hrsh7th marked this pull request as draft January 29, 2023 09:21
if (canReferNonEnumerableProperty) expect(nextState.foo).toBeTruthy()
if (useStrictShallowCopy)
expect(isEnumerable(nextState, "foo")).toBeFalsy()
if (useStrictShallowCopy) expect(nextState.baz).toBeTruthy()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This test is ugly, but after some consideration, I think it should be the spec.

First, I thought that non-enumerable properties should not be visible from drafts when useStrictShallowCopy is false.

However, achieving this behavior requires checking whether the referenced property is non-enumerable, which introduces new overhead.

@hrsh7th hrsh7th marked this pull request as ready for review February 15, 2023 16:43
@hrsh7th
Copy link
Contributor Author

hrsh7th commented Feb 15, 2023

I'm not confident about the documentation part, but I think the code part is ready for review.

@mweststrate mweststrate mentioned this pull request Apr 3, 2023
@mweststrate mweststrate merged commit d533f24 into immerjs:main Apr 17, 2023
@github-actions
Copy link
Contributor

🎉 This PR is included in version 10.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Immer takes a long time to update the data.
4 participants