Performance issues when rendering large number of nested components


#1

We are currently porting backbone + jQuery spaghetti app to Ember/EmberCLI. We are making good progress, and really like the benefits that Ember is providing - the app is becoming much easier to reason about.

However, we have hit quite a serious performance bottleneck in one of our main views, probably related to this issue. We need to display a grid with ~30 columns and ~100 rows. Each individual cell has a very rich content, composed of multiple components, possibly containing additional nested components, {{each}} blocks, etc.

Only ~10 rows are visible in browser viewport simultaneously, so we only need to render ~ 30 X 10 = 300 cells initially; other cells are rendered on demand as user scrolls the grid. However even with lazy-rendering of rows, rendering is taking to long (e.g. ~1 sec for 300 cells on MB Pro), thus initial load and scrolling of grid is performing so badly that it is almost unusable. In this JsBin there is an example of individual cell component and a simple benchmark, demonstrating the perf issue.

We are considering the following work-arounds:

a) Simplify structure of cell component

We could probably simplify structure of individual cell, e.g. avoiding some {{#each}} or component calls, substituting them for e.g {{#if}} blocks.

I do not like this solution as it would make things harder to maintain. Also, components for cell would have to be designed according to perf-tests on specific Ember version - there is no guarantee that specific helpers used in one version of Ember will still be most/adequately performant in next versions.

b) Render HTMLBars template with no databinding

We are using components only to render templates, we do not need any data-binding functionality as data for each cell does is not changing after it is rendered. Also, we do not need Ember to handle any Events/Actions on cell components. However I am not sure how to render component-less/view-less templates with HTMLBars as it seems quite deeply integrated with Ember views/components.

Is it possible to do something like this in Ember/EmberCLI:

domOrString = HTMLBars.render('templateName', cellContext);
myTarget.append(domOrString);

where cellContext contains e.g. Ember.Object or ordinary js object?

c) Use Handlebars templates

Similar to b), however using pure Handlebars templates to render each cell. I have an impression that Handlebars templates are more easily used outside Ember context. I would like for templates to be precompiled on server, so this would require HTMLBars and Handlebars compiler in EmberCLI, not really nice and probably quite some work to configure properly?

d) Wait for issue to be solved in Ember

As we do not have strict deadline for the release of migrated app, we could just continue migrating other parts of the app and then return to this issue if still not fixed in Ember. In the mentioned issue there is not much info yet on whether this is something that will (maybe) be solved in near future. Should Glimmer rendering engine improve perf or this scenario also, or does it affect only re-rendering perf?

Thank you in advance for any suggestions/insights on the given issue!


#2

I have a similar performance issue in the app I work on. We have Chat built in as a core piece of our communication platform and we’ve had major issues with performance as we scaled up our internal usage of the app and now our live usage as well. My temporary solution is to use one component to render the whole list of messages in a chat room, and it does so by overriding the ‘render’ hook and pushing raw html into the buffer. Its a very low level solution, and I hope to be able to kill it off after Glimmer comes out, but it does work and its very fast. I keep a small test app that I use to keep an eye on render speeds here: http://ericlifka.github.io/recursive-vs-each/ It may or may not be useful to anyone else because it’s very focused on ‘chat like’ data and rendering sequential lists in different ways.


#3

This sounds very similar to the issue I faced in Reviewing Ember and wondering about performance issue. You may be able to fix it by reusing your {{#each}} array as shown in that thread. It brought my rendering time down from ~300ms to <1ms.


#4

The issue is probably related, however the solution is probably not applicable in my case as I have problems with initial render time which, not sub-sequential rerenders.


#5

I am going down the same path - implementing custom rendering for the performance-critical parts. To have familiar template syntax I am using raw Handlebars templates for rendering + some preregistered helpers to work nicely in Ember-CLI. I have extracted corresponding Ember-CLI addon, available at https://github.com/jesenko/ember-cli-raw-handlebars (WIP).

Once given perf issue is fixed in Ember, it should not be hard to simply change raw handlebar templates + corresponding context object back to Ember Components.