Perf issue when using in loop to get width/height & set to other element [Update: Invalid issue]
manikantag opened this issue · 8 comments
UPDATE: My original tests are incorrect or at least not the actual scenarios fastdom is meant for.
I originally read the height/width only once and used the same values in loop. So, clearly there is not need of fastdom here.
I've updated my test code to simulate both DOM reads and writes in loop, and very clearly fastdom heloped a LOT. See my comment about fixed tests.
So, this is an invalid issue, and fastdom clearly helping
Below is the original issue I've created:
Hi,
Currently I'm working on a custom CSS rendering engine for a specific need and I'll be querying offsetHeight
/offsetWidth
frequently and will be setting style.height
/style.width
and other style
properties of elements. I'm thinking to use fastdome to see if I can get any perf benefits, but it seems other way.
I've created gist with and without fastdom: https://gist.github.com/manikantag/630288fb67453bb88c2493f8bd46aadd
(Note: I'm using fastdom-promisified with async/await
).
Use below links to preview directly:
With fastdom: https://gist.githack.com/manikantag/630288fb67453bb88c2493f8bd46aadd/raw/fastdom-async.html
Without fastdom: https://gist.githack.com/manikantag/630288fb67453bb88c2493f8bd46aadd/raw/no-fastdom-test.html
When using fastdom, I'm seeing several callstacks in Chrome performance audit, which is not seen when not using fastdom.
Am I missing anything here? I expected completely opposite behavior!
Ok. I think async/await
is the culprit here. I've tried with just callbacks and Promises. Both of them are way better than async/await
(while callback version performed little better than Promise version)
Callback version: https://gist.githack.com/manikantag/630288fb67453bb88c2493f8bd46aadd/raw/fastdom-callbacks.html
Promise version: https://gist.githack.com/manikantag/630288fb67453bb88c2493f8bd46aadd/raw/fastdom-promise.html
Thanks for confirming about Promise
part (so thus async/await
, which is performing way bad... at least now).
I still see the code without fastdom is performing (little) better. Is this because my example is a trivial one? Are there any specific real-world-scenarios where fastdom will benefit my usecase?
I originally wrote fastdom to allow 'black box' UI components to read/write to the global dom without clashing via a shared scheduling layer (fastdom). If you are in full control of your code and don't need to share the space with third-parties/black-boxes, then you will probably squeeze better perf by rolling your own bespoke solution.
That clarifies better. Thanks @wilsonpage
@wilsonpage Excuse me for my bad repro page. The tests I've done all WRONG. I'm just reading the offsetHeight
/offsetWidth
only once and using them for all the 1000 elements created in the loop.
I've created new test pages which both reads and sets the DOM props in loop, which is the general scenario in most cases. And I can clearly see the benefits of fastdom.
Without fastdom (correct test): https://gist.githack.com/manikantag/630288fb67453bb88c2493f8bd46aadd/raw/no-fastdom-correct-test.html
With fastdom (correct test): https://gist.githack.com/manikantag/630288fb67453bb88c2493f8bd46aadd/raw/fastdom-callbacks-correct-test.html
fastdom is clearly the WINNER. It has brought down the Rendering from 233ms to just 30ms. EXCELLENT!
Again, excuse me for my bad tests and confusing you :) Thanks again for excellent work.
Just some note on your promisified example. It is basically "slow" because you wait inside the mutation loop for each element to be added before you queue the next dom change. At this time fastdom thinks that you are already outside of the current mutation phase and queues your DOM change for the next one. This means every element is added in another rAF and not the same rAF as you want to.
The following two examples are simply not equal.
for (let i = 0; i < 10; i++) {
// every queue is immediately processed
readPromise().then(() => {
// read
});
writePromise().then(() => {
// write
});
}
for (let i = 0; i < 10; i++) {
// if i = 1 the readPromise waits for writePromise of i = 0 to be finished before executed.
await readPromise();
// read
await writePromise();
// write
}
So the main problem of your first example is not that you are using promises but that your async
/await
construct is not executed in the way you wanted to write your code in the first place.
@aFarkas You are correct. Thanks for pointing that.