Caching Example

hugging face
performance
pyodide
Learn how caching can speed up your app
Author
Published

January 7, 2024

Introduction

In computing, a cache is a high-speed data storage layer which stores a subset of data, typically transient in nature, so that future requests for that data are served up faster than is possible by accessing the data’s primary storage location.

Caching allows you to efficiently reuse previously retrieved or computed data to speed up your exploration, jobs or apps.

Good caching solutions for Panel are summarized in the table below

Technology Performance Persistant Horizontal Scaling Vertical Scaling Expiration Preloading Comments
panel.cache Very Fast Optional Optional Yes Yes Yes Configurable
panel.state.as_cached Very Fast Optional Optional Yes Yes Yes Configurable
panel.state.cache Very Fast No No Yes No Yes Simple Dict Cache
DiskCache Very Fast Yes No Yes Yes Yes Simple Persistent Cache
Redis Very Fast Yes Yes Yes Yes Yes Server solution. Works well with the distributed task queue Celery

Note that

  • Expiration enables caching data for a period of time for example seconds, minutes, hours or days.
  • Preloading of a cache can be triggered by a cronjob or event.
  • If the cache is persisted, i.e. stored to disk or runs on a server like Redis it can enable caching data across jobs, applications, servers and restarts.

To learn more about caching check out the AWS Caching Overview

App

This app runs entirely in the browser via Pyodide and panel convert.

Open in a new window | Open in Hugging Face Spaces

Code

Show
"""
# Caching Example

See https://awesome-panel.org/resources/caching_example
"""
import time

import hvplot.pandas  # pylint: disable=unused-import
import numpy as np
import pandas as pd
import panel as pn

pn.extension(design="material")

ACCENT_COLOR = "#1f77b4"

np.random.seed([3, 1415])
PERIODS = 1 * 24 * 60  # minutes. I.e. 1 days
DATA = pd.DataFrame(
    {
        "time": pd.date_range("2020-01-01", periods=PERIODS, freq="T"),
        "price": np.random.randn(PERIODS) + 98,
    }
)

def _load_data(frac=0.1):
    time.sleep(0.5 + frac * 0.5)
    return DATA.sample(frac=frac)

def _plot_data(frac=0.1):
    time.sleep(0.5)
    data = _load_data(frac)
    return data.hvplot(x="time", y="price")

@pn.cache(per_session=True, ttl=60*60*24)
def _plot_data_cached(frac):
    return _plot_data(frac)


# Create Widgets
fraction = pn.widgets.FloatSlider(value=0.1, start=0.1, end=1.0, step=0.1, name="Fraction of data")
duration = pn.widgets.StaticText(value="", name="Time to create plot")
use_cache = pn.widgets.Checkbox(value=False, name="Use Cache")
preload_cache = pn.widgets.Button(name="Preload Cache", button_type="primary", disabled=True)
clear_cache = pn.widgets.Button(name="Clear Cache", disabled=True)
preload_progress = pn.widgets.Progress(
    name="Progress", active=False, value=0, max=100, sizing_mode="stretch_width", disabled=True
)

plot_panel = pn.pane.HoloViews(min_height=500, sizing_mode="stretch_both")

# Setup interactivity
def _clear_cache(*_):
    _plot_data_cached.clear()


clear_cache.on_click(_clear_cache)


def _preload_cache(*_):
    for index in range(0, 11, 1):
        frac_ = round(index / 10, 1)
        preload_progress.value = int(frac_ * 100)
        _plot_data_cached(frac_)
    preload_progress.value = 0


preload_cache.on_click(_preload_cache)


@pn.depends(frac=fraction, watch=True)
def _update_plot(frac):
    start_counter = time.perf_counter()

    frac = round(frac, 1)
    if use_cache.value:
        plot = _plot_data_cached(frac)
    else:
        plot = _plot_data(frac)

    end_counter = time.perf_counter()
    duration.value = str(round(end_counter - start_counter, 4)) + " seconds"

    # Please note DiskCache does not cache the options
    plot.opts(color=ACCENT_COLOR, responsive=True)
    plot_panel.object = plot


@pn.depends(use_cache=use_cache, watch=True)
def _update_cache_widgets(use_cache):  # pylint: disable=redefined-outer-name
    disabled = not use_cache
    preload_cache.disabled = disabled
    clear_cache.disabled = disabled
    preload_progress.disabled = disabled


# Layout the app
pn.Column(
    pn.pane.Markdown(
        "# Speed up slow functions with caching", sizing_mode="stretch_width"
    ),
    fraction,
    duration,
    use_cache,
    plot_panel,
    pn.Row(preload_cache, clear_cache,),
    preload_progress,
).servable()

pn.state.onload(lambda: fraction.param.trigger("value"))

Download (right-click, save-as)

Gif

Png

Mp4

Social

Please share on social media. Thanks.

Back to top