Specifying dependency tree traversal

Hello,

I have a somewhat large dash app and it is getting slow to update. I have a bunch of controls that all modify how a central graph/plot is displayed. The problem is that each time I modify one of the controls, the plot updates like 100 times instead of just once.

My controls are linked to each other in multiple layers. For example, I am making 1D plots and you can choose which x to plot against and the min/max values of x. If you change which x you want to plot, the min/max inputs get updated to relevant values for that x. Then the plot is re-created.

As a small example, consider this app:

app = dash.Dash()
# function to plot
def func(x):
    return x[:,0]**2 + np.sin(x[:,1])
# default ranges for x0 & x1
xranges = [[0,1], [-np.pi, np.pi]]
# dropdown to pick which x to plot against
xchooser = dcc.Dropdown(
        id='xchooser',
        options=[{'label':'x0', 'value':'0'},{'label':'x1', 'value':'1'}],
        value='0')
# the user can also modify the ranges manually
minsetter = dcc.Input(id='minsetter', type='number', value=xranges[0][0])
maxsetter = dcc.Input(id='maxsetter', type='number', value=xranges[0][1])

app.layout = html.Div([
        html.Div(xchooser, style={'width':'15%'}),
        html.Div(['Min: ',minsetter,'Max: ',maxsetter]),
        html.Div([dcc.Graph(id='trend_plot')], style={'width':'80%','float':'right'})
    ])

@app.callback(Output('minsetter','value'),[Input('xchooser','value')])
def set_min(xindex):
    return xranges[int(xindex)][0] 
@app.callback(Output('maxsetter','value'),[Input('xchooser','value')])
def set_max(xindex):
    return xranges[int(xindex)][1] 
@app.callback(Output('trend_plot','figure'),
        [Input('xchooser','value'),Input('minsetter','value'),Input('maxsetter','value')])
def make_graph(xindex, xmin, xmax):
    npt = 20
    xgrid = np.zeros((npt,2))
    xgrid[:,int(xindex)] = np.linspace(int(xmin), int(xmax), npt)
    print 'Calling func!'
    f = func(xgrid)
    return Figure(data=[Scatter(x=xgrid[:,int(xindex)], y=f, mode='markers+lines')])

app.run_server()

Whenever I change the xchooser, the terminal output shows that the graph gets re-created twice instead of once:

Calling func!
127.0.0.1 - - [25/Jul/2017 15:26:11] "POST /_dash-update-component HTTP/1.1" 200 -
Calling func!
127.0.0.1 - - [25/Jul/2017 15:26:11] "POST /_dash-update-component HTTP/1.1" 200 -

With a more complicated app (more inputs & more layers), these extra evaluations pile up and make it quite slow. Is there a way to force Dash to only update the graph once everything else is done being updated? It seems like there could be a more efficient dependency tree walk.

Hey @isaaca –

Thanks for reporting! I think that we can consider this a bug. Dash should wait until both minsetter and maxsetter are done updating before updating trend_plot. Note how it doesn’t update trend_plot when xchooser changes: this part of the dependency resolver works correctly. It seems like the case where there are multiple parent notes that have the same “grandparent” node isn’t working correctly. If they had different grandparents, I believe that it would work correctly.

I’ll work on a fix for this issue this week. Thanks again for reporting!

Any updates yet? Still wondering if this can be fixed for faster updating.

At this point, it’s looking like mid-next week at the earliest.

Working on a fix in https://github.com/plotly/dash-renderer/pull/19

1 Like

The fix has been released in dash-renderer v0.9.0. Upgrade with pip install dash-renderer==0.9.0.

Thanks again @isaaca for reporting!

1 Like

Hey @chriddyp - just an FYI, your update looks like it works most* of the time. From my example here (Many Updates On One Page Load. Dash App), usually only one callback is now observed:

Strangely, sometimes 2 outputs are still thrown: (I can’t seem to find a pattern to it)

Not critical, your update improved performance 10x. Much appreciated. Thanks-

1 Like

@emunson - I have another fix in https://github.com/plotly/dash-renderer/pull/21 that might reduce this even further for you. Could you give that a try?

@chriddyp looks about the same. Out of 15 calls I checked, 4 returned doubles. No rhyme or reason I can tell. Really not a huge issue anymore to be honest, just weird

@chriddyp just an update, upgraded to python 3.6, here’s the result:

image

It was just me all along :laughing:

1 Like