Caching Example
hugging face
performance
pyodide
Learn how caching can speed up your app
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.
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"))
Social
Please share on social media. Thanks.