From 1c9dbbbb19e798b3955eaffe18f3adfae64ddf6e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 8 Jul 2020 22:28:14 -0600 Subject: [PATCH] [BREAKING] Convert RoomState's stored state map to a real map Though the dictionary format works fine, it's slow. Access times are around the 1ms range for rooms like HQ, which doesn't seem like much, but when compared to the Map's access time of 0.05ms it's slow. Converting things to a map means we lose index access and have to instead use `.keys()` and `.values()`, both of which return iterables and not arrays. Even doing the `Array.from()` conversion we see times in the 0.05ms range. This is also what makes this a breaking change. Memory-wise there does not appear to be any measurable impact for a large account. --- examples/node/app.js | 9 +++++---- src/models/room-state.js | 29 +++++++++++++---------------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/examples/node/app.js b/examples/node/app.js index 46220a065..90396b56b 100644 --- a/examples/node/app.js +++ b/examples/node/app.js @@ -288,7 +288,7 @@ function printMemberList(room) { } function printRoomInfo(room) { - var eventDict = room.currentState.events; + var eventMap = room.currentState.events; var eTypeHeader = " Event Type(state_key) "; var sendHeader = " Sender "; // pad content to 100 @@ -300,14 +300,15 @@ function printRoomInfo(room) { var contentHeader = padSide + "Content" + padSide; print(eTypeHeader+sendHeader+contentHeader); print(new Array(100).join("-")); - Object.keys(eventDict).forEach(function(eventType) { + eventMap.keys().forEach(function(eventType) { if (eventType === "m.room.member") { return; } // use /members instead. - Object.keys(eventDict[eventType]).forEach(function(stateKey) { + var eventEventMap = eventMap.get(eventType); + eventEventMap.keys().forEach(function(stateKey) { var typeAndKey = eventType + ( stateKey.length > 0 ? "("+stateKey+")" : "" ); var typeStr = fixWidth(typeAndKey, eTypeHeader.length); - var event = eventDict[eventType][stateKey]; + var event = eventEventMap.get(stateKey); var sendStr = fixWidth(event.getSender(), sendHeader.length); var contentStr = fixWidth( JSON.stringify(event.getContent()), contentHeader.length diff --git a/src/models/room-state.js b/src/models/room-state.js index a7d1072ef..cbcf0b29d 100644 --- a/src/models/room-state.js +++ b/src/models/room-state.js @@ -68,9 +68,7 @@ export function RoomState(roomId, oobMemberFlags = undefined) { this.members = { // userId: RoomMember }; - this.events = { - // eventType: { stateKey: MatrixEvent } - }; + this.events = new Map(); // { eventType: { stateKey: MatrixEvent } } this.paginationToken = null; this._sentinels = { @@ -211,14 +209,14 @@ RoomState.prototype.getSentinelMember = function(userId) { * undefined, else a single event (or null if no match found). */ RoomState.prototype.getStateEvents = function(eventType, stateKey) { - if (!this.events[eventType]) { + if (!this.events.has(eventType)) { // no match return stateKey === undefined ? [] : null; } if (stateKey === undefined) { // return all values - return utils.values(this.events[eventType]); + return Array.from(this.events.get(eventType).values()); } - const event = this.events[eventType][stateKey]; + const event = this.events.get(eventType).get(stateKey); return event ? event : null; }; @@ -238,9 +236,8 @@ RoomState.prototype.clone = function() { const status = this._oobMemberFlags.status; this._oobMemberFlags.status = OOB_STATUS_NOTSTARTED; - Object.values(this.events).forEach((eventsByStateKey) => { - const eventsForType = Object.values(eventsByStateKey); - copy.setStateEvents(eventsForType); + Array.from(this.events.values()).forEach((eventsByStateKey) => { + copy.setStateEvents(Array.from(eventsByStateKey.values())); }); // Ugly hack: see above @@ -276,8 +273,8 @@ RoomState.prototype.clone = function() { */ RoomState.prototype.setUnknownStateEvents = function(events) { const unknownStateEvents = events.filter((event) => { - return this.events[event.getType()] === undefined || - this.events[event.getType()][event.getStateKey()] === undefined; + return !this.events.has(event.getType()) || + !this.events.get(event.getType()).has(event.getStateKey()); }); this.setStateEvents(unknownStateEvents); @@ -386,15 +383,15 @@ RoomState.prototype._getOrCreateMember = function(userId, event) { }; RoomState.prototype._setStateEvent = function(event) { - if (this.events[event.getType()] === undefined) { - this.events[event.getType()] = {}; + if (!this.events.has(event.getType())) { + this.events.set(event.getType(), new Map()); } - this.events[event.getType()][event.getStateKey()] = event; + this.events.get(event.getType()).set(event.getStateKey(), event); }; RoomState.prototype._getStateEventMatching = function(event) { - if (!this.events[event.getType()]) return null; - return this.events[event.getType()][event.getStateKey()]; + if (!this.events.has(event.getType())) return null; + return this.events.get(event.getType()).get(event.getStateKey()); }; RoomState.prototype._updateMember = function(member) {