-
Notifications
You must be signed in to change notification settings - Fork 79
Optimize debounced editor events #93
Comments
My proposal might be
|
Do you need to listen to both Another question, not sure about the nature of the code that is running here... do you actually need to debounce this event, or are you just worried about redundant calls in a given tick of the event loop? If so, might it be good enough to run the code in a Assuming you really need the full debounce behavior...
I wonder if I really think it's worth a lot of effort to squeeze out every microsecond on the typing code path. With that in mind, let me throw an idea out there that may go too far. I have not coded this or benchmarked it or anything, so take it with a heap of salt... Since repeatedly clearing and setting timeouts seems to be expensive, I wonder if a "good enough" |
I just did a quick test with this and the results are pretty comparable to the plain lodash one: const debounce2 = (x, delay) => {
return Observable.create(observer => {
let canceled = false;
const debounced = debounce(v => {
if (!canceled) observer.next(v);
}, delay);
const sub = x.subscribe(debounced);
sub.add(() => {
canceled = true;
});
return sub;
});
}; |
After converting everything to use a It's still about 0.5ms for the timer setup, but subsequent keystrokes within the debounce window are free! I don't know if I can feel the difference but this is a really easy change to make so we should probably do it. This was particularly noticeable because we had one source wired up to debounce both text changes and cursor changes (for code highlights)! |
Great to see. It definitely looks better. ✨ Can I ask again why you're handling |
Ah yeah, I've been meaning to migrate our callsites over to using that one
:)
…On Tue, Oct 17, 2017 at 9:53 PM Nathan Sobo ***@***.***> wrote:
Great to see. It definitely looks better. ✨
Can I ask again why you're handling onDidChange in addition to
onDidChangeText? You'd have *much* less impact on multi-cursor
performance if you only used onDidChangeText, as that event is batched.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#93 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAjPYlh-HA2EXvKbLjhk9_WUrOCZ9HWcks5stYRCgaJpZM4P9GaK>
.
|
…ceTime Summary: `Observable.debounceTime` is actually quite inefficient with its usage of `setInterval` / `clearInterval`: if you look at a profile, it will always clear and re-create an interval upon receiving a new event. In contrast, our debounce implementation (like lodash's) re-uses a timer when possible and just resets its timestamp. When the timer fires, we'll create a new timer if necessary. For very hot codepaths where we debounce things like editor events, every millisecond matters. When features like 'code highlight' debounce events from several streams, this can add up! See facebookarchive/atom-ide-ui#93 for the investigation. Reviewed By: matthewwithanm Differential Revision: D6096145 fbshipit-source-id: 3569e2ce1b7cfc9e693962362ff80583de75e7d5
Summary: Due to the inefficiences found in `debounceTime`, I just went through and replaced all the single-argument `debounceTime` with `fastDebounce`. There's a few callsites that also schedule on the animation frame, so I just preserved those. See facebookarchive/atom-ide-ui#93 for the difference in flame charts for a keystroke after this change. Reviewed By: matthewwithanm Differential Revision: D6107843 fbshipit-source-id: 0597fbcfe260ebd15f259882401ed1d7acb75b76
…ceTime Summary: `Observable.debounceTime` is actually quite inefficient with its usage of `setInterval` / `clearInterval`: if you look at a profile, it will always clear and re-create an interval upon receiving a new event. In contrast, our debounce implementation (like lodash's) re-uses a timer when possible and just resets its timestamp. When the timer fires, we'll create a new timer if necessary. For very hot codepaths where we debounce things like editor events, every millisecond matters. When features like 'code highlight' debounce events from several streams, this can add up! See #93 for the investigation. Reviewed By: matthewwithanm Differential Revision: D6096145 fbshipit-source-id: 3569e2ce1b7cfc9e693962362ff80583de75e7d5
Summary: Due to the inefficiences found in `debounceTime`, I just went through and replaced all the single-argument `debounceTime` with `fastDebounce`. There's a few callsites that also schedule on the animation frame, so I just preserved those. See #93 for the difference in flame charts for a keystroke after this change. Reviewed By: matthewwithanm Differential Revision: D6107843 fbshipit-source-id: 0597fbcfe260ebd15f259882401ed1d7acb75b76
Fix is in v0.5.2. |
💥 |
We use RxJS to listen to (and debounce) various editor events. The hottest events are those that occur on every keystroke, namely:
buffer.onDidChange()
buffer.onDidChangeText()
editor.onDidChangeCursorPosition()
/cursor.onDidChangePosition
Unfortunately it seems like RxJS
debounceTime
implementation (which uses itsAsyncScheduler
by default) seems to be relatively inefficient, taking up to a millisecond for each of the above events.Flame graph from @nathansobo of a keypress event:
Zip of @nathansobo's timeline profile: typing.zip
I put together a small benchmark where I create an observable and compare the cost of using
debounceTime
versus just usinglodash.debounce
(hopefully it's at least somewhat realistic):https://gist.github.com/hansonw/f7b7f4b32011ee09916dc27a45a43845
With that in mind, it may make sense to enforce that we use
observableFromSubscribeFunction
in combination with a more standarddebounce
function rather than usingdebounceTime
throughout the Atom IDE / Nuclide codebases.The text was updated successfully, but these errors were encountered: