-
Notifications
You must be signed in to change notification settings - Fork 37.6k
Add Event.defer with hidden experimental setting #163305
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
Conversation
These just schedule things so they are already async, this causes them to all share the same timer to remove that overhead.
|
I found that about 20-30% of keypress time was spend setting up or tearing down timeouts/callback. This change introduces Before: The above was taken using the current change but swapping out defer's implementation with After: This was tested on my fast machine so the numbers are pretty small, they get quite a bit larger on slower hardware. Async stack traces were disabled to get more accurate timeout measurements. |
jrieken
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for kicking this off. It's an interesting approach to debounce at the emitter and not at listeners. I believe either works and I let @alexdima and @hediet decide what they prefer. I do see the advantage of such an offering - it is a nice and central invitation/reminder to be a nice listener
The disposable store for the defer util is important and shouldn't be forgotten
| * block critical work (eg. latency of keypress to text rendered). | ||
| */ | ||
| export function defer(event: Event<unknown>): Event<void> { | ||
| return debounce<unknown, void>(event, () => void 0, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs to have the ability to be created with a disposable store (see other utils and doc) because otherwise a leaked listener keeps this alive
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 will keep in mind for next iteration
|
|
||
| private readonly _onDidChangeModelContent: Emitter<IModelContentChangedEvent> = this._register(new Emitter<IModelContentChangedEvent>({ deliveryQueue: this._deliveryQueue })); | ||
| public readonly onDidChangeModelContent: Event<IModelContentChangedEvent> = this._onDidChangeModelContent.event; | ||
| public readonly onDidChangeModelContentDeferred: Event<void> = Event.defer(this._onDidChangeModelContent.event); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs to pass this._store to dispose this event when the editor is dispsoed
|
|
||
| // feature: update active when cursor changes | ||
| this._outlineDisposables.add(this._editor.onDidChangeCursorPosition(_ => { | ||
| this._outlineDisposables.add(this._editor.onDidChangeCursorPositionDeferred(_ => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This listener is already async (TimeoutTimer#cancelAndSet) and I don't think double debounce is needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was what I was trying to avoid, deferring the extra clear and timeout calls into the existing timeout, but it didn't give the gains expected.
| editor?.onDidChangeModelLanguage(() => this._update(true), this, this._renderDisposables); | ||
| editor?.onDidChangeModelContent(() => this._update(false), this, this._renderDisposables); | ||
| editor?.onDidChangeModelLanguage(() => this._updateLanguage(true), this, this._renderDisposables); | ||
| editor?.onDidChangeModelContentDeferred(() => this._scheduleUpdate(), this, this._renderDisposables); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again double debounce?
|
Making this a draft again, still measuring differences |
|
Some measurements via Typing 'a' on my macbook until line is full (to avoid scrolling overhead), ignore first 10 samples as suggest is involved PR where defer uses debounce PR where defer uses signal main These results show that it may be slightly faster, but not as much as I was expecting. I notice the JavaScript Profiler is far more accurate than the Performance tab in devtools for measuring this low level stuff so I should probably be using that to analyze. It also seems to avoid the timeout overhead problem. Closing this for now, will investigate further. |
Yeah, that matches my experience. Tho, it lacks the holistic view of things |




Part of #161622