How to batch a rendering of multiple lines

Hello!

I am experimenting with the following code. We plot 10 lines with their own datasources on one plot. The streaming happens periodically every second for all lines. Each line has a different amount of points generated within this one second.

from bokeh.layouts import column
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, Dropdown

import numpy as np
import time

class Line:
	def __init__(self, doc, plot, waiting_time):
		self._current_x = time.monotonic()
		self._current_y = 0
		self._new_points = dict(x=[], y=[])
		self._source = ColumnDataSource(data=dict(x=[], y=[]))

		plot.line(source=self._source)
		doc.add_periodic_callback(self._generate_point, waiting_time)

	def _generate_point(self):
		self._current_x = time.monotonic()
		self._current_y += np.random.normal()

		self._new_points["x"].append(self._current_x)
		self._new_points["y"].append(self._current_y)

	def stream(self):
		self._source.stream(self._new_points)

		self._new_points["x"].clear()
		self._new_points["y"].clear()

doc = curdoc()

p = figure(width=300, height=300, tools="pan,reset,save", y_axis_location="right")
lines = []
for i in range(10):
	lines.append(Line(doc, p, 100*i))

def stream_point():
	for line in lines:
		line.stream()

menu = [("Item 1", "item_1"), ("Item 2", "item_2"), ("Item 3", "item_3")]
dropdown = Dropdown(label="Dropdown button", menu=menu)

doc.add_periodic_callback(stream_point, 1000)
doc.add_root(column(p, dropdown))

The problem here is that after some time the browser spends so much time rendering the plot that the dropdown button becomes unusable.

Taking a look at the profiling tools that Firefox offers I can spot that for every time the plot receives a websocket message it redraws the whole plot.You can see on the picture the black line (websocket message) followed by a brown line (rendering).

What would be really great and would solve my problem here is to trigger rendering only after the last websocket message. Since my update loop is self contained I think the simplest solution would be to give me a possibility to trigger the rendering myself.

Like so

plot(automatic_redraw=False)

def stream_point():
	for line in lines:
		line.stream()
        plot.redraw()

Is anything close to that available in bokeh? If not then does this interface make sense? I am happy to make a PR to bokeh if there are no design problems with it.

I do understand that usually all lines should be coming from the same ColumnDataSource and then all lines are updated at the same time but here the hard requirement is that each data source has a different length.

Thank you in advance!

The closest thing would be hold / unhold on curdoc(). I suppose worth a quick try, but I actually don’t think they would prevent multiple renders in this specific case.

I am happy to make a PR to bokeh if there are no design problems with it.

Better to start with a GitHub Issue to start discussion with more of the core team. There’s a few potential approaches I can imagine, but not all of them would be acceptable. We need to get agreement on scope and approach and also input from other folks that may have interest (e.g. the holoviz team)

Thanks for a reply!

I have created a Github issue right here