dash-core-components
dash-core-components copied to clipboard
Loading dropdown component with many items is slow
Similar to the issue described in #103, I am having trouble with the speed of very long dropdown lists (40k+ items). However, in my case it's not the speed of searching the dropdown list (which has been solved in #103), it's rather the time until the component is loaded dynamically.
Network issues should not be at play here, as everything is running locally.
Note the time between choosing to display the dropdown menu and the time until it actually shows up:
(List with 100k items)
Minimal working example
minimal working example is here as a gist.
Dash versions
>conda env export | grep dash
- dash=0.35.1=py_0
- dash-core-components=0.42.0=py_0
- dash-html-components=0.13.4=py_0
- dash-renderer=0.16.1=py_0
- dash-table=3.1.11=py_0
- dash-bootstrap-components==0.3.0
Running this locally, the network call takes ~400ms and the dropdown rendering ~8.5 seconds with the 100k items example above.
Most if not all the initialization time seems to be linked to fast filter options; you are probably hitting the same limit as was mentioned here.
Investigating the filter to get a feel for what is going on here.
Investigation results
So.. we are using react-virtualized-select
for the dropdown rendering and react-select-fast-filter-options
for the underlying search functionality. fast-filter is using js-search
to index the available options.
js-search
creates a index of all label substrings (as labels are tagged as the key to filter on), creating ~700k valid partial filter entries for this dataset, containing roughly 100M references to the original options -- allocating ~600MB of memory... explaining the ~8 seconds load/processing time
The cost of creating the index is linked to the number of entries n
, the number of keys k
, and the length of each key entry e
. Number of entries per item is at worse e*(e+1)/2, giving an overall load time worse case is O(nkc^2).
Conclusion
There is no simple solution to this performance issue. Either we find a more efficient library to take care of the filtering or we implement our own.
Other, hybrid approaches may or may not be compatible with the react-virtualized-select
component. A combination of lazy loading, caching / busting and debouncing could get us a long way. This related to https://github.com/plotly/dash-table/issues/250.
How about TrieSearch? I racall it to work quite well...
But actually, I think a server-side solution would be the way to go, as already the transfer-time is non-negligible and could be a bottleneck when not running locally.
In R shiny, a server-side selectizeInput is implemented, and it works flawlessly for the same usecase.
We actually had someone suggesting something similar (server-side updates to the dropdown options) here. Turns out the react-virtualized-select component does not behave well when its options are changed on the fly / while the dropdown is opened.
Thanks for the TrieSearch suggestion, will look into it.
Moving this issue to the dash-core-components repo.
Just wondering... is there an alternative by now? This issue is basically the only thing preventing me to switch from Shiny to dash...
Also wondering if there is any update or alternative to this issue.
It wouldn't usually be a problem, but my team has a Dash app integrated into flask app by following this guide (the last chapter, using WSGI) and the issue is that the fast parts of the front end are being loaded instantly, while Dropdown menu along with some other components loads after about 15-20 seconds.
I have tried removing the dropdown menu and rendering only other components, and such layout loads instantly.
For reference, the list which should be displayed in a dropdown menu has 12907 items.
Hi, I am also facing performance issue with updating a dropdown with ~10k items. Has anyone solved this issue?
Same here, any solution?
Have folks tried the "Dynamic Options" alternative mentioned in the Dropdown documentation? https://dash.plotly.com/dash-core-components/dropdown
This would be the recommended way forward when dealing with hundreds, thousands, or millions of options.
Thank you for the blazing fast response! Unfortunately in my case I would need a dropdown, if no solution/workaround is possible I will switch, but letting the user both see the dropdown values and being able to search would be better.
@LucaMarconato how many options are trying to display? And by "I need a dropdown", you mean that you need the user to be able to see every option that is available?
I have approximately 1000 options. Long story short (for the other people reading this), I can avoid using the dropdown if it is a problem right now. For completeness in the rest of the comment I explain why in my scenario a dropdown would be, I think, preferable.
I am working with data that it is indexed using a string which is a concatenation of parameters. The best would be to use a better way to index the data but since the data is from other people and other tools have been built from the data using that indexing system, I prefer to just use adhere to their convention. What I am currently implementing is to use a integer index (for instance with a input widget) and when a number is selected I display the original string, the problem is that sometimes a person could be interested in just a subset of the data and so a integer number is not really a "key", and this ambiguity could be a bit confusing/time consuming when multiple people want to collaborate but are not working on slightly different set of items.
Nice, @chriddyp! With the dynamic option available, feel free to close the issue from my side.
I think the issue is not actually in the Plotly dash dropdown or DCC. The issue is with dropdown itself in html you can see many question in the internet like this which are not related Dash. The browser tries to load all the options in its memory so its taking time.
Wanting to bump this issue because we're working with this same problem but would prefer to have the dropdown function as intended as much as possible rather than moving to a dynamic callback altogether, e.g. we want to see all of the options in the dropdown list rather than having to search-and-see.