Repository navigation
Keep the value and the tag of a tracked accessor in its storage - #21675
Draft
NullVoxPopuli-ai-agent wants to merge 1 commit into
Draft
NullVoxPopuli-ai-agent wants to merge 1 commit into
NullVoxPopuli-ai-agent wants to merge 1 commit into
Conversation
The accessor form of @Tracked asked the tag registry for the tag of the field on each read and each write: a WeakMap lookup and a Map lookup. At the first read, the storage of the accessor now gets a cell with the value and the tag. A read or a write after that does no map lookup. The cell takes its tag from the registry, so observers, computed chains and notifyPropertyChange work on the same tag as before. Until the first read, the storage holds the plain value, and a write goes through the registry as before. So an instance whose accessor is never read has no cell and no tag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A read of a
@tracked accessoris about 2 times faster, and a write is about 1.6 times faster. The first read of each accessor of each instance may cost a little more.@tracked accessor count = 0. The field form,@tracked count = 0, does not change. Keep the value and the tag of a tracked field in one cell #21661 and Store each @tracked field in a TrackedValue #21662 are for the field form.maintoo.pnpm benchcannot show this change, because their apps use the field form. The numbers are from a node benchmark of the built package.How it works
This is the "side signal" pattern: the value is plain data, and a tag next to it reports a change.
On
main, the accessor keeps the plain value in its storage and asks the tag registry for the tag on each read and each write. That is aWeakMaplookup and aMaplookup each time.With this PR, the storage of the accessor holds a cell after the first read:
consumeTag(cell.tag). No map lookup.DIRTY_TAG(cell.tag, true). No map lookup for the field. The write to the self tag of the object stays as it is.notifyPropertyChangefind the tag of a key there, so they work on the same tag as before.main. An instance whose accessor is never read has no cell and no tag, as onmain.instanceof. The class is private to the module, so a value of the user cannot be a cell.A first version made the cell when the instance was made. It had the same read and write times, but the construction of 1,000 instances with 4 accessors each took 5 times as long. So the cell is made at the first read.
Benchmark
A node script applies
trackedto a class with four accessors, the way a stage 3 transform does it. Each number is the median over 20 processes for each build. Control ismainat9bec1cb2a8.mainmsHow this was measured
trackedfromdist/prod/packages/@glimmer/tracking/index.jsof the packedember-sourceof each build.main, PR, PR,mainfor each case. 10 rounds.undefined, andnull.Memory
Tests
metal/tests/tracked/accessor_test.js, 10 tests. The repo had no unit test for the accessor form, only a smoke test scenario. The tests apply the decorator by hand, because the legacy decorator build of the tests cannot compile theaccessorkeyword.tagForPropertygave before the first read or write,notifyPropertyChange, a computed property with the accessor as a dependent key, self-assignment, theequalsoption, an array value, and the dev assertion for a write after a read.mainand with this PR.VITE_STABLE_DECORATORS=true.tsc --noEmit, ESLint, Prettier andpnpm lint:docspass.🤖 Generated with Claude Code