Skip to content

Commit 17647cb

Browse files
authored
Merge pull request #12896 from CesiumGS/event-performance
Event performance
2 parents 7bd358c + 202e4c3 commit 17647cb

File tree

4 files changed

+48
-55
lines changed

4 files changed

+48
-55
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- Materials loaded from type now respect submaterials present in the referenced material type. [#10566](https://github.com/CesiumGS/cesium/issues/10566)
1010
- Reverts `createImageBitmap` options update to continue support for older browsers [#12846](https://github.com/CesiumGS/cesium/issues/12846)
1111
- Fix flickering artifact in Gaussian splat models caused by incorrect sorting results. [#12662](https://github.com/CesiumGS/cesium/issues/12662)
12+
- Improved performance and reduced memory usage of `Event` class. [#12896](https://github.com/CesiumGS/cesium/pull/12896)
1213
- Fixes vertical misalignment of glyphs in labels with small fonts [#8474](https://github.com/CesiumGS/cesium/issues/8474)
1314

1415
#### Additions :tada:

packages/engine/Source/Core/Event.js

Lines changed: 41 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,14 @@ import defined from "./defined.js";
2222
* evt.removeEventListener(MyObject.prototype.myListener);
2323
*/
2424
function Event() {
25-
this._listeners = [];
26-
this._scopes = [];
25+
/**
26+
* @type {Map<Listener,Set<object>>}
27+
* @private
28+
*/
29+
this._listeners = new Map();
2730
this._toRemove = [];
2831
this._insideRaiseEvent = false;
32+
this._listenerCount = 0; // Tracks number of listener + scope pairs
2933
}
3034

3135
Object.defineProperties(Event.prototype, {
@@ -37,7 +41,7 @@ Object.defineProperties(Event.prototype, {
3741
*/
3842
numberOfListeners: {
3943
get: function () {
40-
return this._listeners.length - this._toRemove.length;
44+
return this._listenerCount;
4145
},
4246
},
4347
});
@@ -60,8 +64,14 @@ Event.prototype.addEventListener = function (listener, scope) {
6064
Check.typeOf.func("listener", listener);
6165
//>>includeEnd('debug');
6266

63-
this._listeners.push(listener);
64-
this._scopes.push(scope);
67+
if (!this._listeners.has(listener)) {
68+
this._listeners.set(listener, new Set());
69+
}
70+
const scopes = this._listeners.get(listener);
71+
if (!scopes.has(scope)) {
72+
scopes.add(scope);
73+
this._listenerCount++;
74+
}
6575

6676
const event = this;
6777
return function () {
@@ -84,39 +94,24 @@ Event.prototype.removeEventListener = function (listener, scope) {
8494
Check.typeOf.func("listener", listener);
8595
//>>includeEnd('debug');
8696

87-
const listeners = this._listeners;
88-
const scopes = this._scopes;
89-
90-
let index = -1;
91-
for (let i = 0; i < listeners.length; i++) {
92-
if (listeners[i] === listener && scopes[i] === scope) {
93-
index = i;
94-
break;
95-
}
97+
const scopes = this._listeners.get(listener);
98+
if (!scopes || !scopes.has(scope)) {
99+
return false;
96100
}
97101

98-
if (index !== -1) {
99-
if (this._insideRaiseEvent) {
100-
//In order to allow removing an event subscription from within
101-
//a callback, we don't actually remove the items here. Instead
102-
//remember the index they are at and undefined their value.
103-
this._toRemove.push(index);
104-
listeners[index] = undefined;
105-
scopes[index] = undefined;
106-
} else {
107-
listeners.splice(index, 1);
108-
scopes.splice(index, 1);
102+
if (this._insideRaiseEvent) {
103+
this._toRemove.push({ listener, scope });
104+
} else {
105+
scopes.delete(scope);
106+
if (scopes.size === 0) {
107+
this._listeners.delete(listener);
109108
}
110-
return true;
111109
}
112110

113-
return false;
111+
this._listenerCount--;
112+
return true;
114113
};
115114

116-
function compareNumber(a, b) {
117-
return b - a;
118-
}
119-
120115
/**
121116
* Raises the event by calling each registered listener with all supplied arguments.
122117
*
@@ -128,29 +123,26 @@ function compareNumber(a, b) {
128123
Event.prototype.raiseEvent = function () {
129124
this._insideRaiseEvent = true;
130125

131-
let i;
132-
const listeners = this._listeners;
133-
const scopes = this._scopes;
134-
let length = listeners.length;
126+
for (const [listener, scopes] of this._listeners.entries()) {
127+
if (!defined(listener)) {
128+
continue;
129+
}
135130

136-
for (i = 0; i < length; i++) {
137-
const listener = listeners[i];
138-
if (defined(listener)) {
139-
listeners[i].apply(scopes[i], arguments);
131+
for (const scope of scopes) {
132+
listener.apply(scope, arguments);
140133
}
141134
}
142135

143-
//Actually remove items removed in removeEventListener.
144-
const toRemove = this._toRemove;
145-
length = toRemove.length;
146-
if (length > 0) {
147-
toRemove.sort(compareNumber);
148-
for (i = 0; i < length; i++) {
149-
const index = toRemove[i];
150-
listeners.splice(index, 1);
151-
scopes.splice(index, 1);
136+
// Actually remove items marked for removal
137+
if (this._toRemove.length > 0) {
138+
for (const { listener, scope } of this._toRemove) {
139+
const scopes = this._listeners.get(listener);
140+
scopes.delete(scope);
141+
if (scopes.size === 0) {
142+
this._listeners.delete(listener);
143+
}
152144
}
153-
toRemove.length = 0;
145+
this._toRemove.length = 0;
154146
}
155147

156148
this._insideRaiseEvent = false;

packages/engine/Specs/Scene/I3SLayerSpec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,8 @@ describe("Scene/I3SLayer", function () {
275275
const testLayer = mockI3SProvider.layers[0];
276276

277277
expect(testLayer.tileset).toBeDefined();
278-
expect(testLayer.tileset.tileUnload._listeners.length).toEqual(1);
279-
expect(testLayer.tileset.tileVisible._listeners.length).toEqual(1);
278+
expect(testLayer.tileset.tileUnload._listeners.size).toEqual(1);
279+
expect(testLayer.tileset.tileVisible._listeners.size).toEqual(1);
280280

281281
expect(testLayer._rootNode).toBeDefined();
282282
expect(testLayer._rootNode._tile).toBe(testLayer.tileset._root);

packages/engine/Specs/Widget/CesiumWidgetSpec.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,17 +1662,17 @@ describe(
16621662
})
16631663
.then(function () {
16641664
const preMixinListenerCount =
1665-
preMixinDataSource.entities.collectionChanged._listeners.length;
1665+
preMixinDataSource.entities.collectionChanged._listeners.size;
16661666
const postMixinListenerCount =
1667-
postMixinDataSource.entities.collectionChanged._listeners.length;
1667+
postMixinDataSource.entities.collectionChanged._listeners.size;
16681668

16691669
widget = widget.destroy();
16701670

16711671
expect(
1672-
preMixinDataSource.entities.collectionChanged._listeners.length,
1672+
preMixinDataSource.entities.collectionChanged._listeners.size,
16731673
).not.toEqual(preMixinListenerCount);
16741674
expect(
1675-
postMixinDataSource.entities.collectionChanged._listeners.length,
1675+
postMixinDataSource.entities.collectionChanged._listeners.size,
16761676
).not.toEqual(postMixinListenerCount);
16771677
});
16781678
});

0 commit comments

Comments
 (0)