You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
a **dashed** border marks a cell restored from cache, a **solid** one runs live.
99
+
The following examines two concrete examples of how this caching approach pays off in real notebooks.
100
+
Through caching, both of these notebooks are **portable all the way to the browser**, and retain their interactivity.
101
+
The notebook targets 2 real usecases: (1) restoring an expensive state, and (2) pre-computing a state space for sharing or exploration.
103
102
104
-
/// tip | Tip :lightning:
103
+
Boxes below are filled by dispatch branch (<span style="color:#1565c0">Pure</span>, <span style="color:#2e7d32">ContentAddressed</span>, <span style="color:#ef6c00">ExecutionPath</span>); a **dashed** border marks a cell restored from cache, a **solid** one runs live.
104
+
105
+
/// important | New in marimo `0.24.14`
105
106
`marimo export html-wasm --execute` bundles the blobs into static HTML with caching enabled.
106
107
///
107
108
""")
108
109
```
109
110
110
-
### 1 · Restore expensive state · _pytorch → onnx_
111
+
### 1 · Restore expensive state
111
112
112
-
The whole training block is one `persistent_cache`. On the exported page it is a
<spanclass="cap">One persistent_cache block restores a live OnnxRuntime session on a cache hit — torch / pymde / mnist1d never import — while the lasso UI and the in-Python inference run live.</span>
<spanclass="cap"><b>Left:</b> live embed (interactive in the shared island export) — the PyMDE embedding of the held-out set with the region confusion matrix overlaid; both render from cached predictions, torch-free. <b>Right:</b> one persistent_cache block restores a live OnnxRuntime on a cache hit, so torch / pymde / mnist1d never import while the consumers re-run live.</span>
120
123
121
124
<divstyle="display:flex;align-items:center;gap:0.7rem;margin:0.3rem00.2rem;"><imgsrc="public/qr_onnx.png"alt="QR onnx demo"style="height:80px;flex:00auto;"><spanclass="cap"style="margin:0;">📱 <b>Live demo</b> — scan to run the exported WASM notebook in your browser; the model restores from cache with no torch.</span></div>
122
125
123
-
### 2 · Precompute a state space · _KANNs sweep_
126
+
### 2 · Precompute a state space
124
127
125
-
The fit is keyed on the discrete UI *indices*, not their values, so the whole state
126
-
space is enumerable and its result is **torch-free** (numpy + str). A plain
127
-
`product(...)` loop **warms every point** — no compute button — so any
128
-
dropdown/slider pick is then a torch-free cache hit feeding the plot.
128
+
This notebook we demonstrate how caching unlocks easily sharing results.
129
+
The full notebook precomputes a discrete state space of 88 combinations of function, layer scheme, and noise level.
130
+
Caching the results allows the user to explore the full state space in a live embed, with every pick served from cache and no torch required (and share with collaborators!)
<spanclass="cap">A product() sweep warms every entry of the discrete UI state space (88 blobs); the current dropdown/slider pick is then a torch-free cache hit feeding the plot.</span>
<spanclass="cap"><b>Left:</b> live embed (interactive in the shared island export) — pick a function, layer scheme, and noise level; the fit scatter updates from a torch-free cache hit. <b>Right:</b> a product() sweep warms every entry of the discrete UI state space (88 blobs), so any pick is served from cache and only the plot re-runs.</span>
132
137
133
138
<divstyle="display:flex;align-items:center;gap:0.7rem;margin:0.3rem00.2rem;"><imgsrc="public/qr_kanns.png"alt="QR kanns demo"style="height:80px;flex:00auto;"><spanclass="cap"style="margin:0;">📱 <b>Live demo</b> — scan to explore the full precomputed sweep; every dropdown/slider pick is an instant torch-free cache hit.</span></div>
Caching wins only when a **hit beats recompute**. We time the full
140
-
*edit-upstream → value-bound-in-Python* wall clock against the two directly
141
-
comparable systems — **mandala**(SciPy '24; keys via `joblib.hash`) and
142
-
**diskcache**(byte-keyed, the no-derivation floor) — on numpy `float64`
143
-
payloads from **1 MB to 500 MB**.
144
+
Yes! Caching allows for portability in only a way an execution graph can provide.
145
+
However, the concept of caching is not particular to marimo, the following examines alternative approaches to caching in Python scientific computing and compares their performance to marimo's.
146
+
We time the full *edit-upstream to value-bound-in-Python* wall clock against the two directly comparable systems: **mandala**(SciPy '24; keys via `joblib.hash`) and **diskcache**(byte-keyed, the no-derivation floor) using numpy `float64` payloads from **1 MB to 500 MB**.
<spanclass="cap">(a) hit latency vs payload with the 100 ms interactive line and a 90%-hit break-even curve; (b) hit (key+load) vs miss (key+save) at 500 MB; (c) per-call spread. `diskcache.memoize` fails past its SQLite blob ceiling.</span>
caption="held across exploratory payloads", bordered=True),
@@ -153,33 +159,34 @@ mo.hstack([
153
159
], widths="equal")
154
160
```
155
161
156
-

157
-
<spanclass="cap">(a) hit latency vs payload with the 100 ms interactive line and a 90%-hit break-even curve; (b) hit (key+load) vs miss (key+save) at 500 MB; (c) per-call spread. diskcache.memoize fails past its SQLite blob ceiling.</span>
158
-
159
-
### What the numbers say
160
-
161
162
- Every persistent method holds the **100 ms** interactive threshold up to ~49 MB payloads.
162
-
- marimo's hit is **within noise of mandala** at every size and **strictly faster at 500 MB**: it hashes the ndarray's contiguous buffer directly, while mandala pickles first (≈**3×** end-to-end on an M4 Max, ≈**1.2×** on a Linux x86-64 server).
163
-
- Value load tracks the **diskcache floor**— the remaining gap is *key derivation*, not storage.
163
+
- marimo's hit is **within noise of mandala** at every size and **strictly faster at 500 MB**: it hashes the `ndarray`'s contiguous buffer directly, while mandala pickles first (≈**3×** end-to-end on an M4 Max, ≈**1.2×** on a Linux x86-64 server).
164
+
- Value load tracks the **diskcache floor**(diskcache can optionally **not** provide a key derivation).
164
165
-`diskcache.memoize`**fails outright past its SQLite blob ceiling**.
165
166
- At a 90% hit rate, caching pays once the cell body costs more than ≈10% above the hit curve.
166
167
167
-
/// warning | Honest limits
168
-
- Library versions don't enter the key unless `pin_modules=True` → an upgrade can serve a **stale hit**.
168
+
/// warning | Limitations
169
+
- Library versions and python versions must be consistent (this can be loosened with `pin_modules=False`)
169
170
- Mutable refs that **bypass the DAG** (alias/closure mutation) can still poison downstream.
170
-
- Unpickling is **code execution** — a poisoned cache is a real risk (future: Ed25519-signed blobs).
171
+
- Unpickling is **code execution** — a poisoned cache is a real risk but marimo does leverage Ed25519-signed blobs to establish a chain of trust.
171
172
- Side effects are folded in *explicitly* via handles: `mo.watch.file`, `mo.watch.directory`.
172
173
///
173
174
<!---->
174
-
/// tip | Takeaway 🍃
175
-
**Compiled body + content-addressed refs + parent-cell hashes** → a Merkle DAG
176
-
that invalidates at subtree granularity. Native to the reactive notebook,
177
-
**zero user effort**, competitive with scientific-Python memoizers — and
178
-
**portable all the way to the browser.**
179
175
180
-
*Yes, this poster is a live marimo export.*
181
-
///
176
+
# Takeaway 🍃
177
+
178
+
-**Compiled body + content-addressed refs + parent-cell hashes** -> a Merkle DAG
179
+
- caches invalidates at subtree granularity. Native to the reactive notebook.
180
+
-**Zero user effort**, competitive with scientific-Python memoizers — and
<divstyle="text-align:center;flex:00auto;"><imgsrc="public/qr_poster.png"alt="QR poster export"style="height:92px;display:block;margin:0auto;"><spanclass="cap"style="margin:0.1rem00;">scan this poster</span></div>
<divstyle="text-align:center;"><imgsrc="public/qr_poster.png"alt="QR this poster"style="height:84px;display:block;margin:0auto;"><spanclass="cap"style="margin:0.1rem00;">this poster live</span></div>
189
+
<divstyle="text-align:center;"><imgsrc="public/qr_github.png"alt="QR marimo on GitHub"style="height:84px;display:block;margin:0auto;"><spanclass="cap"style="margin:0.1rem00;">github:marimo-team</span></div>
190
+
</div>
191
+
</div>
192
+
<divstyle="display:flex;align-items:center;gap:0.9rem;margin-top:0.7rem;"><imgsrc="public/numfocus.png"alt="NumFOCUS affiliated project"style="height:40px;display:inline-block;flex:00auto;"><spanclass="cap"style="margin:0;">marimo is licensed under Apache 2.0 and free to use · marimo is a NumFOCUS-affiliated project.</span></div>
0 commit comments