I stripped down the example a bit more, leaving just a single hv.Image.
Unfortunately Buffer actually makes it far slower (5fps).
FRAMES = 160
N_BINS = 512
spec_buffer = hv.streams.Buffer(
np.random.rand(FRAMES, N_BINS),
length=FRAMES,
index=False
)
def spec_cb(data):
return hv.Image(data, bounds=(0, 0, FRAMES, N_BINS))
hv.DynamicMap(spec_cb, streams=[spec_buffer])
from pyinstrument import Profiler
profiler = Profiler()
profiler.start()
iters = 10
start = time.time()
last = start
for i in range(iters):
spec_buffer.send(np.random.rand(1, N_BINS))
now = time.time()
last = now
print(f"{iters / (time.time() - start):.2f} fps average")
profiler.stop()
profiler.print()
profiler.open_in_browser()
pyinstrument shows it spending most of the time in push(), JSON encoding and some sort of diffing.
Next, I ran a minimal example with Pipe.
FRAMES = 160
N_BINS = 512
rolling_buffer = np.zeros((N_BINS, FRAMES))
def spec_pipe_cb2(data):
if data is not None:
rolling_buffer[:,:-1] = rolling_buffer[:,1:]
rolling_buffer[:,-1:] = data
return hv.Image(
rolling_buffer,
bounds=(0, 0, FRAMES, N_BINS)
)
spec_pipe = hv.streams.Pipe()
hv.DynamicMap(spec_pipe_cb2, streams=[spec_pipe])
iters = 100
start = time.time()
last = start
for i in range(iters):
spec_pipe.send(np.random.rand(N_BINS, 1))
now = time.time()
last = now
print(f"{iters / (time.time() - start):.2f} fps average")
It runs at 47fps, which is pretty good. However as soon as I add a second plot with a Curve it goes to 27fps. It’s only spending 9% of time in my Pipe callback but everything it does afterwards is immense.
I may next try to use Bokeh directly (without Holoviews) for this real-time use case.
I will say that the performance is a bit noticeable in other situations as well; for example I needed to render 30-40 small plots side-by-side and it took a noticeable amount of time, and with ~300 plots and it was taking upwards of 30 seconds.