Skip to content

Commit 0ba3b56

Browse files
authored
Merge pull request #3009 from Spriteware/optimize-getReadyCallbacks
Performance improvement on (pattern-matching) callbacks
2 parents a354a73 + 59cbffb commit 0ba3b56

File tree

5 files changed

+30
-10
lines changed

5 files changed

+30
-10
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
1010
- [#3011](https://github.com/plotly/dash/pull/3011) Fixed an exception error caused by assigning `None` to array properties with `exact` or `shape` element types. Fixes [#3010](https://github.com/plotly/dash/issues/3010)
1111
- [#2991](https://github.com/plotly/dash/pull/2991) Add support for URL decoding of the search parameter for pages.
1212
- [#3025](https://github.com/plotly/dash/pull/3025) Fix no output callback with error handler setting the response to NoUpdate and triggering an error.
13-
- [#3034](https://github.com/plotly/dash/pull/3034) Remove whitespace from `metadata.json` files to reduce package size
13+
- [#3034](https://github.com/plotly/dash/pull/3034) Remove whitespace from `metadata.json` files to reduce package size.
14+
- [#3009](https://github.com/plotly/dash/pull/3009) Performance improvement on (pattern-matching) callbacks.
1415

1516
## [2.18.1] - 2024-09-12
1617

components/dash-core-components/tests/integration/graph/test_graph_basics.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ def selected_df_figure(selection):
8181

8282
dash_dcc.start_server(app)
8383

84+
dash_dcc.wait_for_element("#graph .js-plotly-plot")
8485
wait.until(lambda: dash_dcc.driver.title == "Dash", timeout=2)
8586
sleep(1)
8687
# TODO: not sure 2 calls actually makes sense here, shouldn't it be 1?

components/dash-core-components/tests/integration/graph/test_graph_varia.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,7 @@ def test_grva008_shapes_not_lost(dash_dcc):
714714
dash_dcc.start_server(app)
715715
button = dash_dcc.wait_for_element("#button")
716716
dash_dcc.wait_for_text_to_equal("#output", "0")
717+
dash_dcc.wait_for_element("#graph .js-plotly-plot")
717718

718719
# Draw a shape
719720
dash_dcc.click_and_hold_at_coord_fractions("#graph", 0.25, 0.25)

dash/dash-renderer/src/actions/dependencies_ts.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,13 +233,27 @@ export const getReadyCallbacks = (
233233
}
234234
}
235235

236+
// Ramda.JS `difference` function is slow because it compares objects entirely
237+
// This cause the following `filter` to be exponentially slow as the number of inputs or outputs grow
238+
// We can optimize this by comparing only the `id+prop` part of the inputs & outputs.
239+
// Original difference takes 380ms on average to compute difference between 200 inputs and 1 output.
240+
// The following function takes 1-2ms on average.
241+
const differenceBasedOnId = (inputs: any[], outputs: any[]): any[] =>
242+
inputs.filter(
243+
input =>
244+
!outputs.some(
245+
output =>
246+
combineIdAndProp(input) === combineIdAndProp(output)
247+
)
248+
);
249+
236250
// Find `requested` callbacks that do not depend on a outstanding output (as either input or state)
237251
// Outputs which overlap an input do not count as an outstanding output
238252
return filter(
239253
cb =>
240254
all<ILayoutCallbackProperty>(
241255
cbp => !outputsMap[combineIdAndProp(cbp)],
242-
difference(
256+
differenceBasedOnId(
243257
flatten(cb.getInputs(paths)),
244258
flatten(cb.getOutputs(paths))
245259
)

dash/dash-renderer/src/observers/prioritizedCallbacks.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {find, flatten, map, partition, pluck, sort, uniq} from 'ramda';
1+
import {find, flatten, map, partition, sort} from 'ramda';
22

33
import {IStoreState} from '../store';
44

@@ -53,13 +53,16 @@ const getStash = (
5353
return {allOutputs, allPropIds};
5454
};
5555

56-
const getIds = (cb: ICallback, paths: any) =>
57-
uniq(
58-
pluck('id', [
59-
...flatten(cb.getInputs(paths)),
60-
...flatten(cb.getState(paths))
61-
])
62-
);
56+
const getIds = (cb: ICallback, paths: any) => {
57+
const items = [
58+
...flatten(cb.getInputs(paths)),
59+
...flatten(cb.getState(paths))
60+
];
61+
62+
const uniqueIds = new Map(items.map(item => [stringifyId(item.id), item]));
63+
const uniqueItems = Array.from(uniqueIds.values());
64+
return uniqueItems;
65+
};
6366

6467
const observer: IStoreObserverDefinition<IStoreState> = {
6568
observer: async ({dispatch, getState}) => {

0 commit comments

Comments
 (0)