How can I do performance debugging?


#1

Situation

I have a SPA with a few pages displaying collections of items. In one of these pages, I have a listing of the items where for each item a summary is displayed as a row item and when that row is clicked, a more detailed version of the row is being displayed inline.

On the development dataset (few items in each collection) everything works alright but when I moved to a production dataset the app (few thousands items in some of the collections) everything slows to a halt. Now, clicking on a row to get the detailed version takes around 1 second. The collections are kept in a session record as Dict with a String Id as key.

What I tried

I have a filter function for the items and I tried to force the listing to only 10 items. This improves the situation a little bit but the app is still quite unresponsive.

The app runs on 0.18 and so I was able to use the instructions from this thread:

The Debug.logTime function proved to be a wonderful investigative tool but it reveled something strange.

I used it like this:

main : Program Flags Model Msg
main =
    Navigation.programWithFlags
        (RouteTo << Route.fromLocation)
        { init = init
        , update = \msg -> Debug.logTime "update" (update msg)
        , view = Debug.logTime "mainView" view
        , subscriptions = subscriptions
        }

This is how the performance recording looks like for two clicks (one to show the details and one to hide them for one of the items):

The largest part of the time seams to be spent in the runtime. For each click there are 3 calls to update that are barely visible. One of the calls can be optimized away by restructuring the code but the problem remains.

Any hints or ideas of what else I could try are welcomed.

LATER EDIT:
the vast majority of the time took by the runtime was due to --debug. Thanks @dmy for the hint.

LATER EDIT2:
It looks like part of the delay that I’m still seeing is caused by the fact that DevTools is open.


#2

I have upgraded the code to 0.19 and, unfortunately, the app is still quite sluggish.

I have used Html.Lazy on the items in the collection and it improved things but still I have what looks like several hundred ms delay per click.

If it makes any difference, document.getElementsByTagName('*').length returns 14054 right now (just to give you an idea about scale). 13873 of those nodes are the descendants of the div that holds the list of rows.

Looking at the Call Tree in the Performance Tab of DevTools, it looks like the bulk of the time (485ms) is spent in _VirtualDom_diff

Can anyone provide any idea about what else could I try in order to improve the performance?


#3

Sounds like it needs something elaborate to put less stuff in the DOM. Like only generating html for the items that are visible in the viewport, and replacing the off-screen ones with empty boxes. (Or two large empty boxes above and below, that vary in size as the user scrolls.) I guess that would involve heavy use of the viewport API from Browser to know what’s on screen or not.
I’ve heard of some of the big tech companies doing things like that in JS. But I’ve never tried it, which is why this is a bit vague!


#4

You mentioned that you use Html.Lazy, but do you also use Html.Keyed ? It could help if you add/remove some element(s) in a very long list.

Also if you cannot implement some paging for some reasons to avoid displaying all the rows, maybe this package could help?

https://package.elm-lang.org/packages/FabienHenon/elm-infinite-list-view/latest/

Note that I never used it.

At last, what do you use for styling?


#5

I hope this example can give you an idea you haven’t tried already :sunny:

https://ellie-app.com/3QT4zQYTTjSa1

I’m curious if the app runs slow even when you’re not rendering them? or is it the DOM?