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

Shrink ComputedValue using a bitfield #3880

Merged
merged 1 commit into from
Jun 13, 2024

Conversation

peterm-canva
Copy link
Contributor

@peterm-canva peterm-canva commented May 21, 2024

Code change checklist

  • [] N/A: Added/updated unit tests
  • [] N/A: Updated /docs. For new functionality, at least API.md should be updated
  • Verified that there is no significant performance drop (yarn mobx test:performance)

ComputedValue is currently 112 bytes in 64-bit chromium browsers, and it's not unreasonable to see 10s or 100s of thousands of these on very large apps. This makes this class IMO a reasonable target for memory optimization, given small changes can give big wins in absolute memory.

This change takes four boolean fields and stores them in one bitfield, adding getters and setters to keep the API to access them the same.

This shrinks 112 bytes down to 100 (verified in devtools) by removing 4x 4 byte booleans and replacing them with 1x 4 byte integer in v8. In Safari the bools take 8 bytes so this should save 3x 8 bytes (but I didn't check).

There will be an increased overhead to get/set the boolean values now given the bit-shifting required. I don't see any regressions running yarn test:performance, but I'm happy to investigate further if this is a concern. I don't think these booleans are in any really performance critical paths. If they are, I'm pretty confident v8 at least will optimize the bit-shifting well, and only add a few machine instructions in optimized code.

Regarding the code, using a bitfield could be done in many different ways, so take what I have here as one suggestion, I'm happy to refactor or make more general. I'd like to avoid introducing a bitfield class, because the allocation of a new object per ComputedValue instance will cancel out the memory savings. Better to keep the bitfield as a direct member of ComputedValue.

If we can reach a suitable solution here, I'd like to do the same thing to ObservableValue, Atom, Reaction and ObservableObjectAdministration which from a brief glance can all save 12-ish bytes as well.

Copy link

changeset-bot bot commented May 21, 2024

🦋 Changeset detected

Latest commit: ff1d3dd

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
mobx Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@peterm-canva
Copy link
Contributor Author

Not that familiar with changesets but this is an implementation detail so I think doesn't need a changeset. Otherwise I'll add one.

@peterm-canva peterm-canva marked this pull request as ready for review May 21, 2024 23:56
@peterm-canva
Copy link
Contributor Author

@urugator are you able to help with review? Many thanks

@peterm-canva peterm-canva force-pushed the smoosh-computed-value branch 2 times, most recently from c437bf7 to 0177cb0 Compare May 29, 2024 02:53
@peterm-canva
Copy link
Contributor Author

Added a patchset because @taj-p mentioned I will most likely need that so we can uprev to that version.

@peterm-canva peterm-canva requested a review from jzhan-canva May 29, 2024 03:22
Copy link
Member

@mweststrate mweststrate 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 great, thanks for adding this! If any perf regressions are reported, I expect we could loose the getter/setters, but hopefully JIT actually erases those.

packages/mobx/src/core/computedvalue.ts Outdated Show resolved Hide resolved
@mweststrate
Copy link
Member

Thanks for this PR, this looks great!

If we can reach a suitable solution here, I'd like to do the same thing to ObservableValue, Atom, Reaction and ObservableObjectAdministration which from a brief glance can all save 12-ish bytes as well.

I imagine it'd be good to have the release soak for a few weeks, if it doesn't raise any issues sounds good :)

@peterm-canva peterm-canva force-pushed the smoosh-computed-value branch from 0177cb0 to 6b15203 Compare May 30, 2024 01:03
@peterm-canva
Copy link
Contributor Author

I imagine it'd be good to have the release soak for a few weeks, if it doesn't raise any issues sounds good :)

Sounds good, I'll get the next PR ready and we can hold off a few weeks.

@peterm-canva peterm-canva force-pushed the smoosh-computed-value branch from 6b15203 to 4504469 Compare May 30, 2024 01:16
@peterm-canva peterm-canva marked this pull request as draft May 31, 2024 05:57
@peterm-canva
Copy link
Contributor Author

There's an issue with this for the production build. The getters/setters are not renamed by minification, but the code that accesses them does get the keys renamed. This means they always come back falsey. Working on that

@kubk kubk marked this pull request as ready for review May 31, 2024 06:29
@kubk kubk marked this pull request as draft May 31, 2024 06:30
@peterm-canva peterm-canva force-pushed the smoosh-computed-value branch from 4504469 to ff1d3dd Compare June 3, 2024 05:59
@@ -4,7 +4,7 @@ const utils = require("../../v5/utils/test-utils")

const { observable, computed, $mobx, autorun } = mobx

const voidObserver = function () { }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I didn't change this on purpose, I think the formatter is just getting excited because I changed other things in this file

@@ -22,8 +22,8 @@ export interface IAtom extends IObservable {
}

export class Atom implements IAtom {
isPendingUnobservation_ = false // for effective unobserving. BaseAtom has true, for extra optimization, so its onBecomeUnobserved never gets called, because it's not needed
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I changed these so that they are no longer mangled (don't end with _). This is so that they still match the new getter/setter names when accessed. Unfortunately the code generated in the final bundle for getters/setters has their name in quotes e.g. "isPendingUnobservation" and name mangling is not smart enough to figure out these are being used as property names.

@peterm-canva peterm-canva marked this pull request as ready for review June 3, 2024 06:03
@peterm-canva
Copy link
Contributor Author

OK, good to go. I had to change names so they aren't mangled any more. See comment in atom.ts.

@peterm-canva peterm-canva requested a review from mweststrate June 3, 2024 06:04
@peterm-canva
Copy link
Contributor Author

Can we land this one?

@mweststrate mweststrate merged commit e9e1955 into mobxjs:main Jun 13, 2024
1 check passed
@github-actions github-actions bot mentioned this pull request Jun 13, 2024
@mweststrate
Copy link
Member

Sorry, missed the update! Merged!

@mweststrate
Copy link
Member

Should soon be available as 6.12.4

@peterm-canva since you are validating performance, I am wondering if you are interested in checking if #3884 also gains these positive results for you?

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.

3 participants