New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blaze speed and memory issues #3596

Closed
lorensr opened this Issue Jan 30, 2015 · 17 comments

Comments

Projects
None yet
10 participants
@lorensr
Collaborator

lorensr commented Jan 30, 2015

(I showed this to @avital at the Devshop and he said I should submit this.)

Blaze is not blazing fast ☺️ Blaze takes ~3.5s to render a document with 30k dom nodes and ~350ms to switch to a template with 100 nodes. I think both take too long (even just 350ms is a noticeable delay for the user).

Reproduction:

http://slow-render-demo.meteor.com/
https://github.com/lorensr/slow-render-demo

Rendering the large template:

screen shot 2015-01-29 at 5 19 55 pm

Small template:

screen shot 2015-01-29 at 5 20 49 pm

These next shots are from a separate run, so numbers are a little different from first two pics. Recording manually while switching between the two templates twice:

image

The first large template takes 2.38s scripting (orange)

image

and 1s rendering (purple)

Compare to jQuery version (node count ~25k):

http://jsfiddle.net/2who62h4/2/

which spends 626ms scripting

image

and 38ms rendering

image

The heap size goes up linearly with the amount of time Blaze spends scripting. While in the simple reproduction above, the heap only gets up to 48MB, a real app has many subtemplates and more complicated data. In my app, a list of just 150 divs (each div more complex than just a number) is 120k nodes, 245MB heap, 10s scripting and 300ms rendering. It consistently crashes iOS Chrome on load. (I assume the OS is killing Chrome due to hitting a heap size limit. It takes half as long and does not crash Chrome when there are 75 things in the list.) Imagine a Twitter app in which you could only read 75 tweets at a time, and then you had to wait while it removed those 75 you read from the screen, garbage collected, and loaded the next 75. My CEO was like, "Well, guess it's time for you to give up on that Meteor thing and rewrite it in native code like that big design firm told us to do." 😭😝

My app:

image
image

@arunoda

This comment has been minimized.

arunoda commented Jan 30, 2015

I hope @chandika is also having this issue.

@chandika

This comment has been minimized.

chandika commented Jan 30, 2015

Yes we do! The GC process when destroying and creating templates takes a huge amount of time and makes apps largely unusable when there is a substantial amount of data.

@avital avital added the Project:Blaze label Feb 2, 2015

@jamiter

This comment has been minimized.

Contributor

jamiter commented Feb 6, 2015

@lorensr: My CEO was like, "Well, guess it's time for you to give up on that Meteor thing and rewrite it in native code like that big design firm told us to do."

I'm afraid my CEO will be like that if he finds out ;) I love meteor, but this might be a show stopper... We need to handle a lot of data and are also seeing performance issues.

@lorensr

This comment has been minimized.

Collaborator

lorensr commented Feb 14, 2015

Another data point – load time for the same page in my app:
Desktop Chrome: 4s
iPhone 6 Safari: 12s
iPhone 6 Cordova: 25s

@avital

This comment has been minimized.

Contributor

avital commented Feb 25, 2015

I'm looking into this now. Looks like 70% of the time is spent diffing the array. This definitely can be optimized.

@avital

This comment has been minimized.

Contributor

avital commented Feb 25, 2015

LocalCollection._idStringify is also a big time sink

avital added a commit that referenced this issue Feb 25, 2015

Improve Blaze performance on initial array rendering
This is one of a longer sequence of changes we can make to make
the diffing algorithm in Blaze be linear as opposed to quadratic
which it is at the momeny, in general.

This change specifically makes it so that the very common case of
an {{#each}} over an array that switches from empty to a long array
much faster, since the adds in those case are always at the end
of the array. This means there's no bookkeeping necessary to
update indexes.

This changes reduces the rendering time from the example posted
on #3596 from 5000ms to 1729ms.
@avital

This comment has been minimized.

Contributor

avital commented Feb 25, 2015

I committed b3c49b5 which reduced the rendering time in the example here from 5000ms to 1729ms. There's lots more we can do!

@lorensr Can you give it a shot? What are the load times for your app on the different browsers now?

@avital

This comment has been minimized.

Contributor

avital commented Feb 25, 2015

@lorensr moreover, what for /you/ would be acceptable page load times for the app that you've linked to, and what numbers do you get now? (I'd like us to have a target for rendering time)

@lorensr

This comment has been minimized.

Collaborator

lorensr commented Feb 26, 2015

Thanks so much for working on this! I've deployed an instance of my app to http://speed.meteor.com/signin and uploaded the same DB as below tests w/ 50 items. Login email is a@b, password a. Your array fix might not improve my app much, since my problem isn't a giant array – it's a normal-sized array with complex subtemplates. In the tests, I go from /new, a small page, to /, a large page by using the top-left link.

The goal for moving between pages in-app is to match native apps, so without any noticeable delay (scientifically 100ms). I'm planning on trying to get this by never removing pages from the DOM, just hiding/showing them when I move between pages. So the remaining timing problem is initial load, where the goal is also matching native. The slowest mainstream apps I can find take 4s (and since Cordova js+render speed seems to be ~6x slower than desktop, that gives a 700ms target for desktop load time). I can cut mine down a lot by initially only loading the 5 out of 50 items that the user can see on the first page. The problem that is harder to deal with is memory. If I load more feed items when I scroll down, to prevent the app from crashing I have to figure out whether I have to pause at some point to let garbage collection go and/or remove from the DOM the items at the top of the list.

1.0.3.1

Mac Chrome while Profiling (adds time overhead):
8.6s, 150k nodes, 380MB heap (70k nodes and 170MB after hitting GC button)

Hand timing:
Mac Chrome: 4s
iOS Chrome: 10s (crashed first test)
iOS Cordova: 28s (Peak 400MB memory and 95% cpu, then down to 0% when done rendering and 200MB after 20s)

devel

Mac Chrome: 5s
iOS Chrome: 11.6s

I didn't expect it to get worse, but I timed it a few times and it was consistent.

@mizzao

This comment has been minimized.

Contributor

mizzao commented Mar 11, 2015

Potential identical (or related) issues were mentioned in this thread:

https://groups.google.com/forum/#!topic/meteor-core/Reb8WgxGURw

@lorensr

This comment has been minimized.

Collaborator

lorensr commented Mar 23, 2015

Interestingly, when I record changing the limit of the list query, eg from 1 to 2, or from 20 to 21, and it only needs to render a single additional item, it takes longer if there are more items already in the list. Going from a list of 1 to a list of 2 takes 100ms scripting and 6ms painting/rendering, but:

20 to 21:
288ms scripting
70 to 71:
398ms scripting

For reference, it takes 2.54s to go from 1 to 71.

Here is the 1 to 2:

image

and exported CPU profile:

https://dl.dropboxusercontent.com/u/27134267/single-list-item.cpuprofile

70 to 71:

@serkandurusoy

This comment has been minimized.

serkandurusoy commented Apr 19, 2015

I now really wish I kept reproducible copies of my code but let me share an experience if it helps. I needed to build a custom reservation calendar that showed 30 days and 12 custom time slots per day. Each slot would have a combination of states (occupied, has-discount-rates, currently-selected, occupied-by-current-user, closed-for-maintenance, prime-time).

I also needed the past slots to be marked as "past" and shown grayed out as time passed. The customer also wanted the days to slide (shift by one day) exactly 1.5 hours after midnight. In their business terms, it was still the same day until 01:30am. Oh and these all had to happen preciesly at the server time within 1minute resolution.

Anyhow, my first stab was to create a monolithic array. It chewed up memory but was still very responsive after the initial load which took around 10 seconds.

But when I loaded up the app on an iphone (ios safari), the app just froze. iOS chrome was a little better, it at least managed to get up an running wihin the first minute.

Then I decided to divide up everything into sub templates. A calendar template that called up "day" templates which called up "slot" templates.

Now it is quite responsive on both desktop and mobile. Initial load is less than a few seconds.

All I chaned was how I assembled the final html. Instead of doing it within one function that generates an array based structure, I divided it up into substructures managed by template helpers.

I hope this helps.

@Batistleman

This comment has been minimized.

Batistleman commented May 21, 2015

any updates on this? I'm rendering tables with ~300 rows, it takes about 3sec. (Not a show stopper, but noticeable)

@mattiLeBlanc

This comment has been minimized.

mattiLeBlanc commented May 22, 2015

Hi, I was asked to post my issue (https://forums.meteor.com/t/list-of-results-with-a-template-per-list-item-very-slow-on-mobile/4683/2) here in this forum.

I have a listing of items (coming from mongo) with an infinite scroll. Each list item has a tap event which opens a slide down below the list item with more information.

I really liked the construct of calling a list item template in the list iterator, like this (JADE format)

div.list
+each episodes
+feedListItem

This creates a template object for each list item with the benefit that the tap event within the list item has access to the THIS scope (which is the whole mongo document of the current iteration).
So this way I dont have to put anything in the markup to transfer data, for example an ID or an URL or anything.
( I picked this up from the meteor tutorial).

There is only one problem, when I start scrolling on an ipad and have for example 50 list results, having so many list item template instances is really slowing down the mobile browser. When I tap on a list item it sometimes takes 2-3 seconds for any response on the tap event. All very laggy.
So I tap, couple seconds nothing happens, than it slides open but I probably already tapped again and it works counter productive. Scrolling becomes slow too

I was a bit worried, cause mobile has to work in my solution. So I gave the issue some thought and figured there are currently a lot of models created with observers and so.

So I ditched the listitem template and just added the markup directly in the EACH block. Now speeds are blazing on mobile, even with 300 results!

+each episodes
                    //- +feedListItem
                    div.listItem(id=_id)
                        div(horizontal layout)
                            div.title( flex)
                                | {{title}}
                            div.date {{ date publishedDate }}

                        div.meta(flex class="{{show _id}}")
                       etc....

But I lost the elegant THIS scope benefit and now have to add data in my markup for the events to pickup.

Is there anyway to use the list item template without having the performance issue, or is that just an inevitably?

|EDIT| I noticed not using the list item template, still gives me the benefit of the this scope per list item. So it is not necessary to add templates per listitem.
But if you need to, for some reason, it does slow down the mobile browser significantly

@bradvogel

This comment has been minimized.

Contributor

bradvogel commented Jun 22, 2015

Additionally, I'm noticing that Blaze uses a setTimeout to render after the DOMContentLoaded event. Is that expected?

@lorensr

This comment has been minimized.

Collaborator

lorensr commented Jul 9, 2015

The forum thread linked in #4674 has some benchmarks that are relevant even when the client has all the data.

@lorensr

This comment has been minimized.

Collaborator

lorensr commented May 20, 2016

We're moving Blaze issues to it's own repo. If you have a reproduction repo that demonstrates a particular render speed issue, please open a new issue there:

https://github.com/meteor/blaze

This FR may help render perf:

meteor/blaze#45

Also check out the Guide on view layer render performance:

http://guide.meteor.com/ui-ux.html#performance

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment