From d4241921617d1eb4c56ca98f11ccd2792c621600 Mon Sep 17 00:00:00 2001 From: Ben Gotow Date: Tue, 12 Jan 2016 09:10:52 -0800 Subject: [PATCH] Perf for thread list. See description - ComponentRegistry: findComponentsMatching often returns zero results but makes a lot of transient data structures. Add a cache to make it O[1] - Contact isMe is called a zillion times to compute thread participant display. Checking the AccountStore had been required was slow. - getStandardCategory is also called a ton. Rather than create a filtered array and then searching that, just search the existing array. --- src/component-registry.coffee | 11 ++++++++++- src/flux/models/contact.coffee | 2 +- src/flux/stores/category-store.coffee | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/component-registry.coffee b/src/component-registry.coffee index d0eee7698..d6c458326 100644 --- a/src/component-registry.coffee +++ b/src/component-registry.coffee @@ -19,6 +19,7 @@ class ComponentRegistry constructor: -> @_registry = {} + @_cache = {} @_showComponentRegions = false @@ -61,6 +62,7 @@ class ComponentRegistry if @_registry[component.displayName] and @_registry[component.displayName].component isnt component throw new Error("ComponentRegistry.register(): A different component was already registered with the name #{component.displayName}") + @_cache = {} @_registry[component.displayName] = {component, locations, modes, roles} # Trigger listeners. It's very important the component registry is debounced. @@ -74,6 +76,7 @@ class ComponentRegistry unregister: (component) => if _.isString(component) throw new Error("ComponentRegistry.unregister() must be called with a component.") + @_cache = {} delete @_registry[component.displayName] @triggerDebounced() @@ -120,6 +123,9 @@ class ComponentRegistry if not descriptor? throw new Error("ComponentRegistry.findComponentsMatching called without descriptor") + cacheKey = JSON.stringify(descriptor) + return @_cache[cacheKey] if @_cache[cacheKey] + {locations, modes, roles} = @_pluralizeDescriptor(descriptor) if not locations and not modes and not roles @@ -140,7 +146,9 @@ class ComponentRegistry return false return true - _.map entries, (entry) -> entry.component + results = _.map entries, (entry) -> entry.component + @_cache[cacheKey] = results + return results triggerDebounced: _.debounce(( -> @trigger(@)), 1) @@ -153,6 +161,7 @@ class ComponentRegistry {locations, modes, roles} _clear: => + @_cache = {} @_registry = {} # Showing Component Regions diff --git a/src/flux/models/contact.coffee b/src/flux/models/contact.coffee index cbe381b6d..7673ffcf7 100644 --- a/src/flux/models/contact.coffee +++ b/src/flux/models/contact.coffee @@ -2,6 +2,7 @@ Model = require './model' Utils = require './utils' Attributes = require '../attributes' RegExpUtils = require '../../regexp-utils' +AccountStore = require '../stores/account-store' _ = require 'underscore' name_prefixes = {} @@ -93,7 +94,6 @@ class Contact extends Model # You should use this method instead of comparing the user's email address to # the account email, since it is case-insensitive and future-proof. isMe: -> - AccountStore = require '../stores/account-store' for account in AccountStore.accounts() if Utils.emailIsEquivalent(@email, account.emailAddress) return true diff --git a/src/flux/stores/category-store.coffee b/src/flux/stores/category-store.coffee index c4da05f08..b4da97c5b 100644 --- a/src/flux/stores/category-store.coffee +++ b/src/flux/stores/category-store.coffee @@ -51,7 +51,7 @@ class CategoryStore extends NylasStore return null unless account? if not name in StandardCategoryNames throw new Error("'#{name}' is not a standard category") - return _.findWhere @standardCategories(account), {name} + return _.findWhere(@categories(account), {name}) # Public: Returns the Folder or Label object that should be used for "Archive" # actions. On Gmail, this is the "all" label. On providers using folders, it