-
-
Notifications
You must be signed in to change notification settings - Fork 35.4k
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
Optimize EventDispatcher performance with Map and Set #29917
Optimize EventDispatcher performance with Map and Set #29917
Conversation
Replaced the use of plain objects and arrays in the EventDispatcher class with Map and Set for storing event listeners. This change is intended to improve performance, particularly in cases with many event listeners, by leveraging the constant time complexity (O(1)) of Map and Set for add, remove, and check operations. - Improved performance in Edge browser, with expected benefits in Chrome as well. - The new implementation maintains compatibility with the EventDispatcher API, so no changes are required in the code that uses it. - This optimization should help reduce overhead and improve event dispatch efficiency, particularly in performance-critical scenarios such as 3D game development. - The code is compatible with the MDN baseline and should work across modern browsers. - Performance benchmarks were conducted using `performance.now()` in a browser, showing measurable improvements in Edge, with similar expectations for Chrome. - No functional change, as the API remains the same.
update class name
📦 Bundle sizeFull ESM build, minified and gzipped.
🌳 Bundle size after tree-shakingMinimal build including a renderer, camera, empty scene, and dependencies.
|
Could you provide us with the numerical results of the test and the code used in the benchmark? |
Please, share links to these benchmarks 🙏 |
import { EventDispatcher as OldEventDispatcher } from "./old.js";
import { EventDispatcher as NewEventDispatcher } from "./new.js";
Deno.bench({
name: "old",
baseline: true,
fn() {
const dispatcher = new OldEventDispatcher();
dispatcher.addEventListener("event", () => {});
dispatcher.dispatchEvent("event");
},
});
Deno.bench({
name: "new",
fn() {
const dispatcher = new NewEventDispatcher();
dispatcher.addEventListener("event", () => {});
dispatcher.dispatchEvent("event");
},
});
|
It makes sense to me. Usually primitive values are faster, this should also apply to |
Just to make sure - this benchmark isn't using
It may still be faster but this "dispatch" call isn't actually doing or testing any actual dispatching right now. I would also benchmark I do still expect the old version to be faster, though. |
Any iterators that use Map and Set stored as linked hash tree and can't be indexed and because of this uses Iteration more important than add/remove listeners, because events can be fired every frame, but listeners assigned only in app start or when something updated by user input. Also classic events list about 10-20 listeners, slice over 10-20 is highly optimised operation. Also Map and Set by designed for frequent update by unknown key set. Events map is KNOWN keyset. Because we not set random key as event type every update. Listeners map will be high optimised type-shape with strictly known properties. Classic event name in core: "update". Conclusion: ... |
Based on the feedback so far it seems better to close the PR. |
Hey, not sure if these changes are what you're looking for, but here's what I did:
I swapped out plain objects and arrays for
Map
andSet
in theEventDispatcher
class to store event listeners. The goal is to boost performance, especially when there are a lot of listeners, sinceMap
andSet
offer O(1) time complexity for adding, removing, and checking items.Key Changes:
Edge
, and we should see similar improvements inChrome
andFirefox
too.EventDispatcher
API, so no need to change anything on the user side I think.performance.now()
in the browser and saw good improvements inEdge
.Chrome
should have similar results.Let me know what you think!