-
-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Description
I brought up in the Gitter today that it might be nice to have built-in support for event delegation. For instance, if I have a list of 1000 buttons, I don't necessarily want to add 1000 click listeners to each one – I can just add one to the parent and let the event bubble up. Presumably this has a perf benefit.
But of course... all perf benefits should be investigated and measured. So I whipped up a quick benchmark to test this out.
This benchmark creates a list of n buttons and then measures the time to add the necessary event listeners, with and without delegation. It also allows you to delay the creation of event listeners to better suss out memory usage, and it prints out the response time using performance.now() - event.timeStamp
(note: not usable in IE/Safari due to lacking high-precision timers for event.timeStamp
). Also note that it only ever creates one function, so it's not measuring the cost of creating multiple functions.
There are 3 main perf aspects we're interested in:
- Time to set up the event listeners themselves
- Memory usage
- Time it takes for the event to bubble to the parent vs just listening on the element itself.
1. Time to set up the event listeners themselves
For this, I tested on an i7 ThinkPad in all browsers except Safari, which I tested on an i5 MacBook Air. Times reported are in milliseconds, median of 5 runs. Note that Safari throttles high-precision timings to 1ms.
Browser | 100 elements | 1000 elements | 10000 elements | 100 elements w/ delegation | 1000 elements w/ delegation | 10000 elements w/ delegation |
---|---|---|---|---|---|---|
Edge 16 | 0.50 | 4.06 | 11.84 | 0.04 | 0.04 | 0.02 |
Chrome 63 | 0.46 | 2.61 | 31.95 | 0.04 | 0.04 | 0.04 |
Firefox 57 | 0.86 | 4.00 | 31.94 | 0.04 | 0.04 | 0.04 |
IE 11 | 1.34 | 11.58 | 31.34 | 0.08 | 0.10 | 0.04 |
Safari 11.0.2 | 0.00 | 1.00 | 12.00 | 0.00 | 0.00 | 0.00 |
So clearly event delegation wins in all browsers pretty handily. This makes sense, because the browser is simply doing less work (i.e. only calling addEventListener()
once as opposed to multiple times). The cost of non-delegation increases as the number of child nodes increases.
2. Memory usage
For analyzing memory, I used Windows Performance Analyzer with the "VirtualAlloc Commit LifeTimes" view, summing the results for each browser process. (Too much detail required to explain all the steps involved, but maybe it's worth a blog post. 😉) I ran the test with 1 run only and 10000 elements, and a delay of 10000ms to better isolate the memory costs.
Here, the results were a bit… odd. Edge and Firefox appear to use much less memory in the delegation scenario versus the non-delegation scenario, which makes sense – there's 1 listener instead of 10000. For Chrome, though, it seems to actually use more memory when you delegate, which surprised me. Results:
- Edge: 3.254MB with delegation, 6.570MB without (50.47% improvement)
- Firefox: 2.383MB with delegation, 8.411MB without (71.67% improvement)
Chrome: 44.34MB with delegation, 32.043MB without (27.74% regression)error: amended below
I was really surprised by what I saw with Chrome, so I ran the test again and got a similar result. This may need more investigation. I also haven't figured out how to analyze memory in Safari.
3. Time it takes for the event to bubble
For this one, I took a cursory glance and observed that the response times seem to be roughly the same with and without event delegation. Maybe this is worth testing on a slower mobile device, or maybe it's worth testing with a very deep hierarchy, but I haven't gone that far yet.
Conclusion
So basically there is more work to do – I need to figure out if the Chrome memory behavior is genuine, and this probably needs to be tested on mobile devices to ensure bubbling doesn't have an exorbitantly high cost, but just based on these preliminary results it seems it's still useful to do event delegation, at least for click listeners.