From cab7a71a9445f14cb12a9c4e2e5f1df1fc3f3064 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 21 Oct 2015 13:25:23 +0100 Subject: [PATCH] Change calculating display names from O(n^2) to O(n) Reduces initial sync times from ~30s to ~1s on accounts with heavily populated rooms. The problem was that f.e. RoomMember it would try to calculate the display name, which involved looping each RoomMember to get their display name to check for disambiguation. We now cache display names to user IDs so we don't need to loop every member when disambiguating. --- lib/models/room-member.js | 15 +++++---------- lib/models/room-state.js | 33 +++++++++++++++++++++++++++++++++ spec/unit/room-member.spec.js | 3 +++ 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/lib/models/room-member.js b/lib/models/room-member.js index 47ae708ba..38d232447 100644 --- a/lib/models/room-member.js +++ b/lib/models/room-member.js @@ -208,18 +208,13 @@ function calculateDisplayName(member, event, roomState) { return displayName; } - var stateEvents = utils.filter( - roomState.getStateEvents("m.room.member"), - function(e) { - return e.getContent().displayname === displayName && - e.getSender() !== selfUserId; - } - ); - if (stateEvents.length > 0) { - // need to disambiguate + var userIds = roomState.getUserIdsWithDisplayName(displayName); + var otherUsers = userIds.filter(function(u) { + return u !== selfUserId; + }); + if (otherUsers.length > 0) { return displayName + " (" + selfUserId + ")"; } - return displayName; } diff --git a/lib/models/room-state.js b/lib/models/room-state.js index 1fe75a62a..b93f7933b 100644 --- a/lib/models/room-state.js +++ b/lib/models/room-state.js @@ -31,6 +31,8 @@ function RoomState(roomId) { // userId: RoomMember }; this._updateModifiedTime(); + this._displayNameToUserIds = {}; + this._userIdsToDisplayNames = {}; } utils.inherits(RoomState, EventEmitter); @@ -108,6 +110,11 @@ RoomState.prototype.setStateEvents = function(stateEvents) { self.events[event.getType()] = {}; } self.events[event.getType()][event.getStateKey()] = event; + if (event.getType() === "m.room.member") { + _updateDisplayNameCache( + self, event.getStateKey(), event.getContent().displayname + ); + } self.emit("RoomState.events", event, self); }); @@ -180,11 +187,37 @@ RoomState.prototype.getLastModifiedTime = function() { return this._modified; }; +RoomState.prototype.getUserIdsWithDisplayName = function(displayName) { + return this._displayNameToUserIds[displayName] || []; +}; + /** * The RoomState class. */ module.exports = RoomState; + +function _updateDisplayNameCache(roomState, userId, displayName) { + var oldName = roomState._userIdsToDisplayNames[userId]; + delete roomState._userIdsToDisplayNames[userId]; + if (oldName) { + var existing = roomState._displayNameToUserIds[oldName] || []; + for (var i = 0; i < existing.length; i++) { + if (existing[i] === userId) { + existing.splice(i, 1); + i--; + } + } + roomState._displayNameToUserIds[oldName] = existing; + } + + roomState._userIdsToDisplayNames[userId] = displayName; + if (!roomState._displayNameToUserIds[displayName]) { + roomState._displayNameToUserIds[displayName] = []; + } + roomState._displayNameToUserIds[displayName].push(userId); +} + /** * Fires whenever the event dictionary in room state is updated. * @event module:client~MatrixClient#"RoomState.events" diff --git a/spec/unit/room-member.spec.js b/spec/unit/room-member.spec.js index f170f7d21..5289fdd6a 100644 --- a/spec/unit/room-member.spec.js +++ b/spec/unit/room-member.spec.js @@ -201,6 +201,9 @@ describe("RoomMember", function() { }), joinEvent ]; + }, + getUserIdsWithDisplayName: function(displayName) { + return [userA, userC]; } }; expect(member.name).toEqual(userA); // default = user_id