Poor scaling when embedding many widgets into the Jinja template

I’ve been seeing poor runtime performance with my Bokeh application. After seeing bokeh version >0.12.10 incredibly slow, and noticing that my Console is giving me a lot of warnings about layout computes taking a long time, I decided to try using Bootstrap to layout the application, embedding individual Bokeh components into it.

This greatly improved the runtime performance. However, the loading time of the website is now quite abysmal. Running Chrome’s Lighthouse Audit (Optimize website speed - Chrome Developers), I see that the issue is the initial server response time. Basically, it’s taking a long time for the server to deliver the HTML page.

In debugging the issue, I’ve found that this embedding method works well when there are only a few Bokeh components, but it scales poorly as the number of Bokeh components increases, getting very slow once there are a few hundred components (and in my application, there are indeed a few hundred components).

I illustrate this issue here: https://github.com/EfremBraun/bokeh-scaling-demo. I took the Movies example from https://github.com/bokeh/bokeh/tree/branch-3.0/examples/app/movies and made some changes. original-0 was taken directly from the example, with 0 new widgets added, original-100 added 100 additional Slider widgets, and original-200 added 200 additional Slider widgets. embed-0 is the original example, but using embedding/Bootstrap for the display; embed-100 took that and added 100 additional Slider widgets, and embed-200 added 200 additional Slider widgets.

When I run bokeh serve original-0, open the application in a Chrome Incognito tab, and run the Lighthouse Audit, I get a 65 performance score. bokeh serve original-100 stays at 65, and bokeh serve original-200 finally goes down to 64. However, while bokeh serve embed-0 gets a 62 performance score (already lower than the original page, though not so bad), bokeh serve embed-100 gets a 60, and bokeh serve original-200 gets a 56, and the page load time is noticeably slower.

Any idea why this is happening and if there’s a way around this?

I’m just going to be up front and state that hundreds of components is not anything that was ever envisioned when Bokeh was being created and developed. The motivation for creating Bokeh was to provide a Python-based alternative to simple R Shiny dashboards, which in my experience typically have a few ~dozens of components at most. So the short answer as to why it might be happening is that that use-case was never historically considered in scope and no effort or resources have ever been put into ensuring that that use-case functions to any particular level of performance.

That said, work towards a 3.0 release is ongoing and a large part of that aims to even further reduce the layout computations that Bokeh tries to do, in favor of letting the browser handle the things it is best at. So that may bear on this, and it’s possible the use-case could be explicitly considered as work progresses. But all I can suggest at this point is to make a GitHub Issue with as much detail as possible so that I can ping the relevant folks to it.

Issue posted at https://github.com/bokeh/bokeh/issues/11707.

1 Like

I believe I figured out an effective solution to this issue.

What slows down the bokeh server is the recompute() function call in https://github.com/bokeh/bokeh/blob/branch-3.0/bokeh/document/models.py. By wrapping the curdoc().add_root() calls within a with curdoc().models.freeze(): block, the recompute() function only gets called once at the very end. e.g., replace:

curdoc().add_root(desc)
curdoc().add_root(p)
for control in controls:
     curdoc().add_root(control)

with:

with curdoc().models.freeze():
    curdoc().add_root(desc)
    curdoc().add_root(p)
    for control in controls:
        curdoc().add_root(control)

Just noting what I did in the issue: that it was a mistake to ever make freeze a public API, and using may result in unexpected or undesirable behavior. I’m glad it is a possible WAR for this issue, and if it satisfies your needs, great. But please know that is 100 percent YMMV usage, and may be deprecated to get it out of public view, if/when there is a proper solution.