Skip to content

Performance issue after v0.40.0 #707

Closed

Description

@popes01a

We recently switched to a newer Dash version and noticed our application got considerably slower. While in Dash v0.40.0 it usually took ~20s for a page to load, in the newer versions this goes up to ~55s, or even 90s depending on the browser.

After some digging, it turns out that having a plain HTML table built using dash-html-components considerably slows the page down. We cannot use a dash-table in this case as we need links inside the table.

There is a considerable difference between v0.40.0 and all versions released after, so I'm assuming that whatever is slowing this down was added in v0.40.0. Page load timings for different dash versions and the code examples can be found below.

Any ideas why this became an issue and what can be done to fix this? Thanks!

dash v0.39.0; dash-core-components v0.44.0; dash-html-components v0.14.0; dash-renderer v0.20.0:
	- Example 1: ~1s
	- Example 2: ~2s

dash v0.40.0; dash-core-components v0.45.0; dash-html-components v0.15.0; dash-renderer v0.21.0:
	- Example 1: ~1s
	- Example 2: ~2s

dash v0.41.0; dash-core-components v0.46.0; dash-html-components v0.15.0; dash-renderer v0.22.0:
	- Example 1: ~1s
	- Example 2: ~9-10s

dash v0.42.0; dash-core-components v0.47.0; dash-html-components v0.16.0; dash-renderer v0.23.0:
	- Example 1: ~1s
	- Example 2: ~9-10s

Example 1:

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

app = dash.Dash()

options = list({'label': 'City %d' % i, 'value': 'city-%d' % i} for i in range(10))

children = []
for i in range(5):
    children.extend([
        html.Label('Multi-Select Dropdown'),
        dcc.Loading(dcc.Dropdown(id='dropdown-%d' % i, options=options, value=['city-0', 'city-1'], multi=True)),
    ])

app.layout = html.Div(children)

for i in range(4):
    @app.callback(
        [Output('dropdown-%d' % (i + 1), 'options'), Output('dropdown-%d' % (i + 1), 'value')],
        [Input('dropdown-%d' % i, 'value')])
    def update_options(value):
        return [options, value]


if __name__ == '__main__':
    app.run_server(debug=True)

Example 2:

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

app = dash.Dash()

options = list({'label': 'City %d' % i, 'value': 'city-%d' % i} for i in range(10))

children = []
for i in range(5):
    children.extend([
        html.Label('Multi-Select Dropdown'),
        dcc.Loading(dcc.Dropdown(id='dropdown-%d' % i, options=options, value=['city-0', 'city-1'], multi=True)),
    ])

children += [html.Tr([html.Td('Label'), html.Td(html.A('?action=enable'))])] * 800

app.layout = html.Div(children)

for i in range(4):
    @app.callback(
        [Output('dropdown-%d' % (i + 1), 'options'), Output('dropdown-%d' % (i + 1), 'value')],
        [Input('dropdown-%d' % i, 'value')])
    def update_options(value):
        return [options, value]


if __name__ == '__main__':
    app.run_server(debug=True)

Activity

  1. 0x26res commented on Aug 8, 2019

    @0x26res

    I'm having the same issue. Updating big html tables has got considerably slower since after 0.40. I had to roll back to 0.40 for now.

  2. byronz commented on Aug 8, 2019

    @byronz
    Contributor

    @popes01a thanks for the elaborated examples for performance issues. there is a major change around 0.40.0 in dash-renderer about the react component hierachy, extra props-check might adds few latency, we will investigate this furthermore.

    Few suggestions you can do before that:

    1. use dash-table
    2. try the client side callback
    3. optimization can also be done on the python side
      children += [html.Tr([html.Td('Label '), html.Td(html.A('?action=enable'))]) for _ in range(800)] will save about 2 seconds from backend.
  3. freshwuzhere commented on Dec 4, 2019

    @freshwuzhere

    Same issue when I upgraded. My render time went from 0.17 seconds to 3 .1 seconds. Simple tabular data page with 20+ callbacks. Rolled back to 0.40.0 and running fine but major functionality roll back also.

  4. added this to the Dash v1.8 milestone on Dec 4, 2019
  5. alexcjohnson commented on Dec 4, 2019

    @alexcjohnson
    Collaborator

    Belated thanks @popes01a for the very clear investigation and report. We will try to dig into this for the next release - which is also expected to reduce the need for these big explicit html.Table components by allowing links and images in dash-table but there will of course always be cases that warrant large explicit layouts of one form or another.

  6. popes01a commented on Dec 5, 2019

    @popes01a
    Author

    I have switched to using a Dropdown and a Continue button to trigger the action for now, but links inside a dash-table component would be ideal. Looking forward to the release!

  7. Marc-Andre-Rivet commented on Jan 10, 2020

    @Marc-Andre-Rivet
    Contributor

    Testing out multiple versions of Dash with code modified from example 2 (multi-output was added in 0.39) and to avoid React warnings.

    import dash
    import dash_core_components as dcc
    import dash_html_components as html
    from dash.dependencies import Input, Output
    
    app = dash.Dash()
    
    options = list({'label': 'City %d' % i, 'value': 'city-%d' % i} for i in range(10))
    
    children = []
    for i in range(5):
        children.extend([
            html.Label('Multi-Select Dropdown'),
            dcc.Dropdown(id='dropdown-%d' % i, options=options, value=['city-0', 'city-1'], multi=True),
        ])
    
    rows = [html.Tr([
        html.Td('Label'),
        html.Td(html.A('?action=enable'))
    ])] * 800
    
    children += [html.Table([html.Tbody(rows)])]
    
    app.layout = html.Div(children)
    
    for i in range(4):
        @app.callback(
            Output('dropdown-%d' % (i + 1), 'options'),
            [Input('dropdown-%d' % i, 'value')])
        def update_options(value):
            return options
    
        @app.callback(
            Output('dropdown-%d' % (i + 1), 'value'),
            [Input('dropdown-%d' % i, 'value')])
        def update_options(value):
            return value
    
    
    
    if __name__ == '__main__':
        app.run_server(debug=False)
    

    I get the following times for debug=False and dropdown count=5, average of 3 runs.

    Dash version Network ready CPU time
    0.35 6.67s 9.44s
    0.40 2.14s 3.27s
    0.42 6.57s 8.60s
    1.7.0 7.24s 9.36s

    Looks like 0.35, 0.42, 1.7.0 all have similar performance and 0.40 stands on its own. Will look into running the perf test against more versions and looking more deeply into what's happening during render that would explain the performance differences.

  8. 5 remaining items

  9. removed this from the Dash Q1/2020 milestone on Feb 6, 2020
  10. 0x26res commented on Jun 24, 2020

    @0x26res

    I've played around with the latest version of dash, and I don't notice the performance issue I was experiencing with 0.41.0+ any more. Maybe this issue has been solved somehow?

    dash==1.13.3
    dash-html-components==1.0.3
    
  11. alexcjohnson commented on Jun 24, 2020

    @alexcjohnson
    Collaborator

    @ArthurAndres great! We did improve performance a bunch in #1254 - @Marc-Andre-Rivet when you're back you can extend the table above, and perhaps close this issue?

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3backlogbugsomething brokenperformancesomething is slowregressionthis used to work

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions