Skip to content

Commit

Permalink
Merge pull request #3009 from Spriteware/optimize-getReadyCallbacks
Browse files Browse the repository at this point in the history
Performance improvement on (pattern-matching) callbacks
  • Loading branch information
T4rk1n authored Oct 15, 2024
2 parents a354a73 + 59cbffb commit 0ba3b56
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 10 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
- [#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)
- [#2991](https://github.com/plotly/dash/pull/2991) Add support for URL decoding of the search parameter for pages.
- [#3025](https://github.com/plotly/dash/pull/3025) Fix no output callback with error handler setting the response to NoUpdate and triggering an error.
- [#3034](https://github.com/plotly/dash/pull/3034) Remove whitespace from `metadata.json` files to reduce package size
- [#3034](https://github.com/plotly/dash/pull/3034) Remove whitespace from `metadata.json` files to reduce package size.
- [#3009](https://github.com/plotly/dash/pull/3009) Performance improvement on (pattern-matching) callbacks.

## [2.18.1] - 2024-09-12

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def selected_df_figure(selection):

dash_dcc.start_server(app)

dash_dcc.wait_for_element("#graph .js-plotly-plot")
wait.until(lambda: dash_dcc.driver.title == "Dash", timeout=2)
sleep(1)
# TODO: not sure 2 calls actually makes sense here, shouldn't it be 1?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,7 @@ def test_grva008_shapes_not_lost(dash_dcc):
dash_dcc.start_server(app)
button = dash_dcc.wait_for_element("#button")
dash_dcc.wait_for_text_to_equal("#output", "0")
dash_dcc.wait_for_element("#graph .js-plotly-plot")

# Draw a shape
dash_dcc.click_and_hold_at_coord_fractions("#graph", 0.25, 0.25)
Expand Down
16 changes: 15 additions & 1 deletion dash/dash-renderer/src/actions/dependencies_ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,13 +233,27 @@ export const getReadyCallbacks = (
}
}

// Ramda.JS `difference` function is slow because it compares objects entirely
// This cause the following `filter` to be exponentially slow as the number of inputs or outputs grow
// We can optimize this by comparing only the `id+prop` part of the inputs & outputs.
// Original difference takes 380ms on average to compute difference between 200 inputs and 1 output.
// The following function takes 1-2ms on average.
const differenceBasedOnId = (inputs: any[], outputs: any[]): any[] =>
inputs.filter(
input =>
!outputs.some(
output =>
combineIdAndProp(input) === combineIdAndProp(output)
)
);

// Find `requested` callbacks that do not depend on a outstanding output (as either input or state)
// Outputs which overlap an input do not count as an outstanding output
return filter(
cb =>
all<ILayoutCallbackProperty>(
cbp => !outputsMap[combineIdAndProp(cbp)],
difference(
differenceBasedOnId(
flatten(cb.getInputs(paths)),
flatten(cb.getOutputs(paths))
)
Expand Down
19 changes: 11 additions & 8 deletions dash/dash-renderer/src/observers/prioritizedCallbacks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {find, flatten, map, partition, pluck, sort, uniq} from 'ramda';
import {find, flatten, map, partition, sort} from 'ramda';

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

Expand Down Expand Up @@ -53,13 +53,16 @@ const getStash = (
return {allOutputs, allPropIds};
};

const getIds = (cb: ICallback, paths: any) =>
uniq(
pluck('id', [
...flatten(cb.getInputs(paths)),
...flatten(cb.getState(paths))
])
);
const getIds = (cb: ICallback, paths: any) => {
const items = [
...flatten(cb.getInputs(paths)),
...flatten(cb.getState(paths))
];

const uniqueIds = new Map(items.map(item => [stringifyId(item.id), item]));
const uniqueItems = Array.from(uniqueIds.values());
return uniqueItems;
};

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

0 comments on commit 0ba3b56

Please sign in to comment.