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

Improve performance of recomputeDependents #2363

Merged
merged 15 commits into from
Jan 31, 2024

Conversation

samkline
Copy link
Contributor

Related Issues or Discussions

N/A

Summary

Hi. I've been working with @edkimmel on performance improvements to our app. It's a React Native ecommerce app that heavily relies on Jotai for managing most state and data. A hotspot that stood out recently was recomputeDependents—that's how Eddie identified the Hermes memory leak (#2355) as a source of our problems.

Even with Eddie's workaround in place for that memory leak, we still found that recomputeDependents was very slow for us, so we took a closer look at what recomputeDependents is doing. It's something like this:

  • loop1 is O(n*m) where n is the number of atoms and m is the number of dependency edges.
  • loop2 is O(n*n*m) because it does the same thing as loop1 but also iterates over the dependencyMap for each iteration where the atom is unchanged (that is, roughly n*m times).

This can be improved by doing a topological sort of the atom dependency graph, which is O(n). This PR changes the recomputeDependents implementation to do that topsort. All existing tests pass.

I ran through a test case for our app that represents a common customer flow. The total accumulated time in recomputeDependents is as follows:

Platform Time before Time after Percent improvement
Web/Chrome 1,483ms 295ms 80%
iOS React Native (Hermes) 3,581ms 367ms 90%
Android React Native (Hermes) 4,137ms 402ms 90%

Thanks again for all of your work on Jotai, we're big fans!

Check List

  • yarn run prettier for formatting code and docs

Copy link

vercel bot commented Jan 24, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
jotai ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jan 31, 2024 2:56pm

Copy link

codesandbox-ci bot commented Jan 24, 2024

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 0433733:

Sandbox Source
React Configuration
React TypeScript Configuration
React Browserify Configuration
React Snowpack Configuration
Next.js Configuration
Next.js with custom Babel config Configuration
React with custom Babel config Configuration

src/vanilla/typeUtils.ts Outdated Show resolved Hide resolved
Copy link
Member

@dai-shi dai-shi left a comment

Choose a reason for hiding this comment

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

Thanks for working on this!
Yes, I was pretty sure that there should be better algorithm then loop1&loop2.
So great to see this.
Some comments below:

src/vanilla/store.ts Outdated Show resolved Hide resolved
src/vanilla/store.ts Outdated Show resolved Hide resolved
src/vanilla/store.ts Outdated Show resolved Hide resolved
@samkline
Copy link
Contributor Author

Thanks for the quick review @dai-shi ! I updated the branch with your comments, and this should fix the lint violations as well.

src/vanilla/store.ts Outdated Show resolved Hide resolved
Copy link
Member

@dai-shi dai-shi left a comment

Choose a reason for hiding this comment

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

Looks great. A few minor comments:

src/vanilla/store.ts Outdated Show resolved Hide resolved
src/vanilla/store.ts Outdated Show resolved Hide resolved
@samkline
Copy link
Contributor Author

For context on the latest commit (4cb7b83), see my comment in #2368 (reply in thread):

Nice find @cltnschlosser ! I just updated the PR #2363 with a fix. For those out of the loop, the issue applies when the code is transpiled to support older JS versions. The transpiled code has an issue with using Array.prototype.push with the Set iterator, such that the push does nothing. It worked fine in modern JS environments. This was the fix: 4cb7b83

@dai-shi
Copy link
Member

dai-shi commented Jan 28, 2024

For context on the latest commit (4cb7b83), see my comment in #2368 (reply in thread):

Nice. Can you please add comment in source code so that we know it's the workaround for transpilation?

@samkline
Copy link
Contributor Author

All set @dai-shi , and I've rebased on top of the latest main

Copy link
Member

@dai-shi dai-shi left a comment

Choose a reason for hiding this comment

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

LGTM
Thanks so much for your contribution!

I will merge this when I prepare the next release.

@samkline
Copy link
Contributor Author

@dai-shi Could you please take one more look? I found another performance optimization, specifically, step 1 where we build allAtomsInDependencyGraph was entirely unnecessary. This is because recomputeDependents only ever has exactly one "root" atom in the graph (that is, one node without incoming edges), and we already know which atom it is. This is a nice simplification and avoids the code that had the transpilation issue earlier as well. The change is 94fe613 .

Copy link
Member

@dai-shi dai-shi left a comment

Choose a reason for hiding this comment

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

It looks simplified!

src/vanilla/store.ts Outdated Show resolved Hide resolved
src/vanilla/store.ts Outdated Show resolved Hide resolved
@yf-yang
Copy link
Contributor

yf-yang commented Jan 31, 2024

jotai-scope is good with 94e356b

@dai-shi dai-shi merged commit 59e04ab into pmndrs:main Jan 31, 2024
33 checks passed
@samkline samkline deleted the recompute-dependents-topsort branch February 1, 2024 03:03
davydkov referenced this pull request in likec4/likec4 Feb 7, 2024
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [jotai](https://togithub.com/pmndrs/jotai) | [`^2.6.0` ->
`^2.6.4`](https://renovatebot.com/diffs/npm/jotai/2.6.0/2.6.4) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/jotai/2.6.4?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/jotai/2.6.4?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/jotai/2.6.0/2.6.4?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/jotai/2.6.0/2.6.4?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>pmndrs/jotai (jotai)</summary>

### [`v2.6.4`](https://togithub.com/pmndrs/jotai/releases/tag/v2.6.4)

[Compare
Source](https://togithub.com/pmndrs/jotai/compare/v2.6.3...v2.6.4)

Performance improvement! Check it out!

##### What's Changed

- refactor: refactoring store and options type by
[@&#8203;ssi02014](https://togithub.com/ssi02014) in
[https://github.com/pmndrs/jotai/pull/2360](https://togithub.com/pmndrs/jotai/pull/2360)
- refactor: modified Args type by
[@&#8203;ssi02014](https://togithub.com/ssi02014) in
[https://github.com/pmndrs/jotai/pull/2367](https://togithub.com/pmndrs/jotai/pull/2367)
- Improve performance of recomputeDependents by
[@&#8203;samkline](https://togithub.com/samkline) in
[https://github.com/pmndrs/jotai/pull/2363](https://togithub.com/pmndrs/jotai/pull/2363)
- fix(vanilla): fix unexpected cache in jotai-scope by
[@&#8203;yf-yang](https://togithub.com/yf-yang) in
[https://github.com/pmndrs/jotai/pull/2371](https://togithub.com/pmndrs/jotai/pull/2371)

##### New Contributors

- [@&#8203;ssi02014](https://togithub.com/ssi02014) made their first
contribution in
[https://github.com/pmndrs/jotai/pull/2360](https://togithub.com/pmndrs/jotai/pull/2360)
- [@&#8203;samkline](https://togithub.com/samkline) made their first
contribution in
[https://github.com/pmndrs/jotai/pull/2363](https://togithub.com/pmndrs/jotai/pull/2363)
- [@&#8203;yf-yang](https://togithub.com/yf-yang) made their first
contribution in
[https://github.com/pmndrs/jotai/pull/2371](https://togithub.com/pmndrs/jotai/pull/2371)

**Full Changelog**:
pmndrs/jotai@v2.6.3...v2.6.4

### [`v2.6.3`](https://togithub.com/pmndrs/jotai/releases/tag/v2.6.3)

[Compare
Source](https://togithub.com/pmndrs/jotai/compare/v2.6.2...v2.6.3)

Some improvements in core and utils 👏

#### What's Changed

- fix: atoms should not remount on recalculations (with async
dependencies) by [@&#8203;dmaskasky](https://togithub.com/dmaskasky) in
[https://github.com/pmndrs/jotai/pull/2347](https://togithub.com/pmndrs/jotai/pull/2347)
- fix(utils): atomWithReducer for jotai-scope by
[@&#8203;dai-shi](https://togithub.com/dai-shi) in
[https://github.com/pmndrs/jotai/pull/2351](https://togithub.com/pmndrs/jotai/pull/2351)
- fix(utils/atomWithStorage): defaultStorage with disabled local storage
crashes on mount by
[@&#8203;benediktschlager](https://togithub.com/benediktschlager) in
[https://github.com/pmndrs/jotai/pull/2354](https://togithub.com/pmndrs/jotai/pull/2354)
- feat(vanilla): customizable atom.unstable_is by
[@&#8203;dai-shi](https://togithub.com/dai-shi) in
[https://github.com/pmndrs/jotai/pull/2356](https://togithub.com/pmndrs/jotai/pull/2356)

#### New Contributors

- [@&#8203;SpringHgui](https://togithub.com/SpringHgui) made their first
contribution in
[https://github.com/pmndrs/jotai/pull/2320](https://togithub.com/pmndrs/jotai/pull/2320)
- [@&#8203;sakurawen](https://togithub.com/sakurawen) made their first
contribution in
[https://github.com/pmndrs/jotai/pull/2358](https://togithub.com/pmndrs/jotai/pull/2358)
- [@&#8203;JoltCode](https://togithub.com/JoltCode) made their first
contribution in
[https://github.com/pmndrs/jotai/pull/2357](https://togithub.com/pmndrs/jotai/pull/2357)
- [@&#8203;benediktschlager](https://togithub.com/benediktschlager) made
their first contribution in
[https://github.com/pmndrs/jotai/pull/2354](https://togithub.com/pmndrs/jotai/pull/2354)

**Full Changelog**:
pmndrs/jotai@v2.6.2...v2.6.3

### [`v2.6.2`](https://togithub.com/pmndrs/jotai/releases/tag/v2.6.2)

[Compare
Source](https://togithub.com/pmndrs/jotai/compare/v2.6.1...v2.6.2)

Some improvements for atomWithStorage. Feedback is welcome.

#### What's Changed

- fix(vanilla): should mount once with atom creator atom by
[@&#8203;nogaten](https://togithub.com/nogaten) in
[https://github.com/pmndrs/jotai/pull/2319](https://togithub.com/pmndrs/jotai/pull/2319)
- feat(utils): createJSONStrage options by
[@&#8203;dai-shi](https://togithub.com/dai-shi) in
[https://github.com/pmndrs/jotai/pull/2324](https://togithub.com/pmndrs/jotai/pull/2324)
- feat(utils): add withStorageValidator by
[@&#8203;dai-shi](https://togithub.com/dai-shi) in
[https://github.com/pmndrs/jotai/pull/2336](https://togithub.com/pmndrs/jotai/pull/2336)

#### New Contributors

- [@&#8203;L-Qun](https://togithub.com/L-Qun) made their first
contribution in
[https://github.com/pmndrs/jotai/pull/2318](https://togithub.com/pmndrs/jotai/pull/2318)
- [@&#8203;ahme-dev](https://togithub.com/ahme-dev) made their first
contribution in
[https://github.com/pmndrs/jotai/pull/2332](https://togithub.com/pmndrs/jotai/pull/2332)
- [@&#8203;nogaten](https://togithub.com/nogaten) made their first
contribution in
[https://github.com/pmndrs/jotai/pull/2319](https://togithub.com/pmndrs/jotai/pull/2319)

**Full Changelog**:
pmndrs/jotai@v2.6.1...v2.6.2

### [`v2.6.1`](https://togithub.com/pmndrs/jotai/releases/tag/v2.6.1)

[Compare
Source](https://togithub.com/pmndrs/jotai/compare/v2.6.0...v2.6.1)

This version has two minor improvements for library authors. It's
wonderful to see Jotai ecosystem growing. No major bugs have been
reported lately. It's fairly okay to say the current version is pretty
stable.

#### What's Changed

- fix(utils): add init property to the return type of atomWithReset by
[@&#8203;jaesoekjjang](https://togithub.com/jaesoekjjang) in
[https://github.com/pmndrs/jotai/pull/2304](https://togithub.com/pmndrs/jotai/pull/2304)
- fix(utils): add description for the RESET symbol. by
[@&#8203;MiroslavPetrik](https://togithub.com/MiroslavPetrik) in
[https://github.com/pmndrs/jotai/pull/2307](https://togithub.com/pmndrs/jotai/pull/2307)

#### New Contributors

- [@&#8203;wherehows](https://togithub.com/wherehows) made their first
contribution in
[https://github.com/pmndrs/jotai/pull/2270](https://togithub.com/pmndrs/jotai/pull/2270)
- [@&#8203;benson00077](https://togithub.com/benson00077) made their
first contribution in
[https://github.com/pmndrs/jotai/pull/2284](https://togithub.com/pmndrs/jotai/pull/2284)
- [@&#8203;ioExpander](https://togithub.com/ioExpander) made their first
contribution in
[https://github.com/pmndrs/jotai/pull/2280](https://togithub.com/pmndrs/jotai/pull/2280)
- [@&#8203;jaesoekjjang](https://togithub.com/jaesoekjjang) made their
first contribution in
[https://github.com/pmndrs/jotai/pull/2304](https://togithub.com/pmndrs/jotai/pull/2304)

**Full Changelog**:
pmndrs/jotai@v2.6.0...v2.6.1

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/likec4/likec4).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4xNzMuMCIsInVwZGF0ZWRJblZlciI6IjM3LjE3My4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiJ9-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
dai-shi added a commit that referenced this pull request May 21, 2024
…n with #2363) (#2534)

* chore(tests): add case against stale values to dependents

* Skip stale conditional dependents test if USE_STORE2

* Simplify out setting dataAtom

* fix store.ts

* fix store2.ts

* refactor

---------

Co-authored-by: Daishi Kato <dai-shi@users.noreply.github.com>
Co-authored-by: daishi <daishi@axlight.com>
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.

5 participants