Skip to content

Caching

marimo comes with utilities to cache intermediate computations. These utilities come in two types: caching the return values of expensive functions in memory, and caching the values of variables to disk.

Caching expensive functions

Use mo.cache to cache the return values of functions in memory, based on the function arguments, closed-over values, and the notebook code defining the function.

The resulting cache is similar to functools.cache, but with the benefit that mo.cache won't return stale values (because it keys on closed-over values) and isn't invalidated when the cell defining the decorated function is simply re-run (because it keys on notebook code). This means that like marimo notebooks, mo.cache has no hidden state associated with the cached function, which makes you more productive while developing iteratively.

For a cache with bounded size, use mo.lru_cache.

marimo.cache

cache(
    fn: Optional[Callable[..., Any]] = None,
    pin_modules: bool = False,
    loader: LoaderPartial | LoaderType = MemoryLoader,
) -> _cache_call
cache(
    name: str,
    pin_modules: bool = False,
    loader: LoaderPartial
    | Loader
    | LoaderType = MemoryLoader,
) -> _cache_context
cache(
    name: Union[str, Optional[Callable[..., Any]]] = None,
    *args: Any,
    loader: Optional[Union[LoaderPartial, Loader]] = None,
    _frame_offset: int = 1,
    **kwargs: Any
) -> Union[_cache_call, _cache_context]

Cache the value of a function based on args and closed-over variables.

Decorating a function with @mo.cache will cache its value based on the function's arguments, closed-over values, and the notebook code.

Usage.

import marimo as mo


@mo.cache
def fib(n):
    if n <= 1:
        return n
    return fib(n - 1) + fib(n - 2)

mo.cache is similar to functools.cache, but with three key benefits:

  1. mo.cache persists its cache even if the cell defining the cached function is re-run, as long as the code defining the function (excluding comments and formatting) has not changed.
  2. mo.cache keys on closed-over values in addition to function arguments, preventing accumulation of hidden state associated with functools.cache.
  3. mo.cache does not require its arguments to be hashable (only pickleable), meaning it can work with lists, sets, NumPy arrays, PyTorch tensors, and more.

mo.cache obtains these benefits at the cost of slightly higher overhead than functools.cache, so it is best used for expensive functions.

Like functools.cache, mo.cache is thread-safe.

The cache has an unlimited maximum size. To limit the cache size, use @mo.lru_cache. mo.cache is slightly faster than mo.lru_cache, but in most applications the difference is negligible.

Note, mo.cache can also be used as a drop in replacement for context block caching like mo.persistent_cache.

Args:

  • pin_modules: if True, the cache will be invalidated if module versions differ.

marimo.lru_cache

lru_cache(
    fn: Optional[Callable[..., Any]] = None,
    maxsize: int = 128,
    pin_modules: bool = False,
) -> _cache_call
lru_cache(
    name: str, maxsize: int = 128, pin_modules: bool = False
) -> _cache_call
lru_cache(
    name: Union[str, Optional[Callable[..., Any]]] = None,
    maxsize: int = 128,
    *args: Any,
    **kwargs: Any
) -> Union[_cache_call, _cache_context]

Decorator for LRU caching the return value of a function.

mo.lru_cache is a version of mo.cache with a bounded cache size. As an LRU (Least Recently Used) cache, only the last used maxsize values are retained, with the oldest values being discarded. For more information, see the documentation of mo.cache.

Usage.

import marimo as mo


@mo.lru_cache
def factorial(n):
    return n * factorial(n - 1) if n else 1

Caching variables to disk

Use mo.persistent_cache to cache variables computed in an expensive block of code to disk. The next time this block of code is run, if marimo detects a cache hit, the code will be skipped and your variables will be loaded into memory, letting you pick up where you left off.

Cache location

By default, caches are stored in __marimo__/cache/, in the directory of the current notebook. For projects versioned with git, consider adding **/__marimo__/cache/ to your .gitignore.

marimo.persistent_cache

persistent_cache(
    name: str,
    save_path: str | None = None,
    pin_modules: bool = False,
) -> _cache_context
persistent_cache(
    fn: Optional[Callable[..., Any]] = None,
    save_path: str | None = None,
    pin_modules: bool = False,
) -> _cache_call
persistent_cache(
    name: Union[str, Optional[Callable[..., Any]]] = None,
    save_path: str | None = None,
    *args: Any,
    **kwargs: Any
) -> Union[_cache_call, _cache_context]

Cache interface to persistently pickle values and load them depending on notebook state.

Can be used as a context manager or a decorator. Mainly expected to be used as a decorator due to expensive overhead. Refer to mo.cache for equivalent functionality on how to use as this as a decorator.