Skip to content

Dash slow pattern-matching performance on many elements #3008

Open

Description

Hello,
First, thank you for your amazing work on Dash.

Describe your context

dash                      2.17.1
dash_ag_grid              31.2.0
dash-bootstrap-components 1.4.1
dash-core-components      2.0.0
dash-html-components      2.0.0
dash-table                5.0.0
jupyter-dash              0.4.2

Browser: Firefox / Chrome (not related) on MacOS 14 (not related either).

Describe the bug

I recently came across a performance limitation of Dash that really made my app slow, and at some point, unusable.
The idea is that I needed to have N checkboxes that my user can check or uncheck. N can sometimes be 20, but it could actually go up to 500 or 1000. That’s where I hit some performance issue with Dash.

Expected behavior

I expect to have N > 1.000 checkboxes rendered in my webpage with no struggle. A similar Vanilla JavaScript code can handle more than 100.000 checkboxes before the page starts to get slow. I imagine that using React comes with some performance downgrade, but it seems to be optimizable.

Screenshots

Here is a visual example for the problem (for 500) ; you can see a lag between my clicks and checkboxes actually being checked:
dash slow

Reproducible code

We create 500 checkboxes and get their value with one callback.

import uuid
import random 
import dash
from dash import dcc, html
from dash.dependencies import Input, Output, ALL

app = dash.Dash(__name__)

# We create 500 checkboxes 
checkboxes = [
    dcc.Checklist(
        options=[{"label": f"Checkbox {i+1}", "value": "checked"}],
        id={"type": "checkbox", "group": random.choice(["a", "b", "c"]), "index": str(uuid.uuid4())},
        inline=True
    ) for i in range(500)
]

app.layout = html.Div([
    html.Div(id="output", style={"position": "fixed", "top": 0, "left": "200px"}),
    html.Div(checkboxes)
])

# Client-side callback to illustrate that network is not the bottleneck here
app.clientside_callback(
    """
    function(checkbox_values, checkbox_ids) {
        const groupCounts = { a: 0, b: 0, c: 0 };

        checkbox_values.forEach((value, i) => {
            if (value && value.includes('checked')) {
                const group = checkbox_ids[i].group;
                groupCounts[group]++;
            }
        });

        const totalChecked = groupCounts.a + groupCounts.b + groupCounts.c;
        return `${totalChecked} checkboxes checked (Group A: ${groupCounts.a}, Group B: ${groupCounts.b}, Group C: ${groupCounts.c})`;
    }
    """,
    Output("output", "children"),
    Input({"type": "checkbox", "group": ALL, "index": ALL}, "value"),
    Input({"type": "checkbox", "group": ALL, "index": ALL}, "id"),
)


# Run the app
if __name__ == "__main__":
    app.run_server(debug=True, port=8060)

The issue is not with the callback itself, but occurs prior to the callback. It just takes some time to gather all the checkboxes, and then fires the callback (which runs fast).

Here are some things I noticed:

  • performance drops after 100s of elements.
  • it’s still slow even if I get rid of the groups and replace string indexes with integers
  • CPU is between 90-100% and at some point the browser may ask to stop the script
  • the problem is not related to the browser. I tested on Chrome and Firefox, recent versions.
  • a similar vanilla JavaScript page (with pattern-matching-like IDs being parsed to JSON) can handle more than 100,000 checkboxes without any performance drop…

Here is the performance report (Firefox). I clicked 4 times, the CPU was around 90% all the time, making the page unresponsive:

image

One of the bottlenecks seems to be getReadyCallbacks from dash_renderer.

I hope this can be solved!

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

Metadata

Assignees

Labels

P3backlogbugsomething brokenperformancesomething is slow

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions