App performance caching and multithreading

Hi Streamlit community,
I recently made an app that displays a lot of images in two columns. Now the performance is not as fast as expected because of two things:
-lack of multithreading
-cache not working fully

The lack of multithreading makes the initial loading of the images quite slow. I tried to solve it with a concurrent.futures.ThreadPoolExecutor but when I call a method with it, it fails to display anything but does display the following error in the terminal:

Thread ‘ThreadPoolExecutor-3_4’: missing ReportContext

For the caching it works only partially because it does not load every image again, but it does rebuild the UI elements such as the sliders etc. which makes the app unresponsive for a long time.

Attached here is a small code example demonstrating what I am trying to do:

Hi @Thiemo_Seys -

For your first issue with missing ReportContext, please see the following post:

In terms of rebuilding the UI elements, I would suggest moving those outside of your function calls, which should minimize any remounting.

Best,
Randy

Hello @randyzwitch , thanks for the explanation.

Is there a way to couple the add_report_ctx to a threadPoolExecutor or is this only possible when manually creating threads?

On the second part of your answer regarding the rebuilding of UI elements, is there any example code for this? I don’t realy understand how I would create a dynamic UI if the UI is not build in any function call.

I’ve had success looping through the ThreadPoolExecutor's _threads attribute to add the context to all its threads right after submitting a task, so something like this:

executor = ThreadPoolExecutor()
for param in params:
    executor.submit(thread_function, params)
    for thread in executor._threads:
        add_report_ctx(thread)

Potentially running add_report_ctx() multiple times on the same thread isn’t ideal conceptually, but it appears to simply add an attribute to the thread object, so it shouldn’t cause anything to break, at least.

This doesn’t work since warnings are popped at executor.submit(...) which happens before add_report_ctx(thread). Thoughts?

With Python 3.7 and up I’ve used the pretty janky workaround of putting a short sleep in the initializer function that the ThreadPoolExecutor() takes – That allows the add_report_ctx() call to be made before the thread does any “real” work.

Not a great solution, of course, but the initializer function isn’t called with the spawned thread as one of its arguments, so we can’t put the add_report_ctx() call directly in there, unfortunately.