Skip to content

Commit f25914e

Browse files
committed
stash: initial draft
1 parent 6b7cdb4 commit f25914e

1 file changed

Lines changed: 55 additions & 48 deletions

File tree

presentations/posters/madisetti_cache/cache_poster.md

Lines changed: 55 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -92,59 +92,65 @@ hashing source alone misses side effects. Computing a key requires a cascade:
9292
<li>Dolstra. The Purely Functional Software Deployment Model (Nix). PhD thesis, 2006.</li>
9393
</ol>
9494

95-
```python {.marimo column="1" hide_code="true" name="in_practice"}
95+
```python {.marimo column="1" hide_code="true"}
9696
mo.md(r"""
9797
## In practice
9898
99-
Two concrete notebooks, two ways the cache pays off — both **portable all the way
100-
to the browser**. Boxes below are filled by dispatch branch (<span style="color:#1565c0">**Pure**</span>,
101-
<span style="color:#2e7d32">**ContentAddressed**</span>, <span style="color:#ef6c00">**ExecutionPath**</span>);
102-
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.
103102
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`
105106
`marimo export html-wasm --execute` bundles the blobs into static HTML with caching enabled.
106107
///
107108
""")
108109
```
109110

110-
### 1 · Restore expensive state · _pytorch → onnx_
111+
### 1 · Restore expensive state
111112

112-
The whole training block is one `persistent_cache`. On the exported page it is a
113-
**cache hit**`torch` / `pymde` / `mnist1d` never import — yet `runtime` restores
114-
as a **live `OnnxRuntime` session** (moutils' stub keeps only its ONNX bytes).
115-
Inference still runs *in Python* (`onnxruntime-web`); edit the model or its data
116-
and the key changes, so it retrains *only then*.
113+
In this notebook we train a simple 1D MNIST torch classifier and cache the model weights.
114+
While which could be easily done by explicitly saving the model to disk, with cached runtime it's just implicit.
115+
A fun consequence is that we can run this expensive notebook in browser.
116+
**pytorch does not have an emscripten target**, but cache restoration leverages OnnxRuntime-WASM.
117117

118-
<div style="text-align:center;margin:0.4rem 0 0;"><img src="public/dag_onnx.svg" alt="Restore-state DAG" style="width:80%;display:inline-block;"></div>
119-
<span class="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>
118+
<div style="display:flex;gap:0.8rem;align-items:center;margin:0.4rem 0 0;">
119+
<div style="flex:0 0 40%;"><img src="public/embed_onnx.png" alt="ONNX embed" style="width:100%;display:block;border-radius:6px;"></div>
120+
<div style="flex:1;"><img src="public/dag_onnx.svg" alt="Restore-state DAG" style="width:100%;display:block;"></div>
121+
</div>
122+
<span class="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>
120123

121124
<div style="display:flex;align-items:center;gap:0.7rem;margin:0.3rem 0 0.2rem;"><img src="public/qr_onnx.png" alt="QR onnx demo" style="height:80px;flex:0 0 auto;"><span class="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>
122125

123-
### 2 · Precompute a state space · _KANNs sweep_
126+
### 2 · Precompute a state space
124127

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!)
129131

130-
<div style="text-align:center;margin:0.4rem 0 0;"><img src="public/dag_kanns.svg" alt="Precompute-sweep DAG" style="width:80%;display:inline-block;"></div>
131-
<span class="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>
132+
<div style="display:flex;gap:0.8rem;align-items:center;margin:0.4rem 0 0;">
133+
<div style="flex:0 0 40%;"><img src="public/embed_kanns.png" alt="KANNs embed" style="width:100%;display:block;border-radius:6px;"></div>
134+
<div style="flex:1;"><img src="public/dag_kanns.svg" alt="Precompute-sweep DAG" style="width:100%;display:block;"></div>
135+
</div>
136+
<span class="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>
132137

133138
<div style="display:flex;align-items:center;gap:0.7rem;margin:0.3rem 0 0.2rem;"><img src="public/qr_kanns.png" alt="QR kanns demo" style="height:80px;flex:0 0 auto;"><span class="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>
134139

135140
```python {.marimo column="2" hide_code="true" name="eval_intro"}
136141
mo.md(r"""
137-
## Does it pay off?
142+
## Is it worth it?
138143
139-
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&nbsp;'24; keys via `joblib.hash`) and
142-
**diskcache** (byte-keyed, the no-derivation floor) — on numpy `float64`
143-
payloads from **1&nbsp;MB to 500&nbsp;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&nbsp;'24; keys via `joblib.hash`) and **diskcache** (byte-keyed, the no-derivation floor) using numpy `float64` payloads from **1&nbsp;MB to 500&nbsp;MB**.
144147
""")
145148
```
146149

147-
```python {.marimo hide_code="true" name="eval_stats"}
150+
![Cache evaluation](public/fig3_cache_eval.svg)
151+
<span class="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>
152+
153+
```python {.marimo hide_code="true" name="results"}
148154
mo.hstack([
149155
mo.stat("< 100 ms", label="interactive threshold",
150156
caption="held across exploratory payloads", bordered=True),
@@ -153,33 +159,34 @@ mo.hstack([
153159
], widths="equal")
154160
```
155161

156-
![Cache evaluation](public/fig3_cache_eval.svg)
157-
<span class="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-
161162
- 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 (≈**** 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 (≈**** 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).
164165
- `diskcache.memoize` **fails outright past its SQLite blob ceiling**.
165166
- At a 90% hit rate, caching pays once the cell body costs more than ≈10% above the hit curve.
166167

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`)
169170
- 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.
171172
- Side effects are folded in *explicitly* via handles: `mo.watch.file`, `mo.watch.directory`.
172173
///
173174
<!---->
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.**
179175

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
181+
- **portable all the way to the browser.**
182+
183+
*This poster is a live marimo export.*
184+
182185
<div style="display:flex;align-items:center;justify-content:space-between;margin-top:1.2rem;gap:1rem;">
183-
<div style="text-align:left;"><img src="public/coreweave_logo.svg" alt="Coreweave" style="height:30px;display:inline-block;vertical-align:middle;"> &nbsp;&nbsp;&nbsp; <img src="public/marimo_logo.png" alt="marimo" style="height:52px;display:inline-block;vertical-align:middle;"> &nbsp;&nbsp;&nbsp; <img src="public/scipy_logo.svg" alt="SciPy" style="height:52px;display:inline-block;vertical-align:middle;"> &nbsp;&nbsp;&nbsp; <img src="public/scipy2026_loon.png" alt="SciPy 2026" style="height:52px;display:inline-block;vertical-align:middle;"></div>
184-
<div style="text-align:center;flex:0 0 auto;"><img src="public/qr_poster.png" alt="QR poster export" style="height:92px;display:block;margin:0 auto;"><span class="cap" style="margin:0.1rem 0 0;">scan this poster</span></div>
185-
</div>
186+
<div style="text-align:left;"><img src="public/coreweave_logo.svg" alt="Coreweave" style="height:40px;display:inline-block;vertical-align:middle;"> &nbsp;&nbsp;&nbsp;&nbsp; <img src="public/marimo_logo.png" alt="marimo" style="height:52px;display:inline-block;vertical-align:middle;"> &nbsp;&nbsp;&nbsp;&nbsp; <img src="public/scipy_logo.svg" alt="SciPy" style="height:52px;display:inline-block;vertical-align:middle;"> &nbsp;&nbsp;&nbsp;&nbsp; <img src="public/scipy2026_loon.png" alt="SciPy 2026" style="height:52px;display:inline-block;vertical-align:middle;"></div>
187+
<div style="display:flex;gap:1.3rem;align-items:flex-start;flex:0 0 auto;">
188+
<div style="text-align:center;"><img src="public/qr_poster.png" alt="QR this poster" style="height:84px;display:block;margin:0 auto;"><span class="cap" style="margin:0.1rem 0 0;">this poster live</span></div>
189+
<div style="text-align:center;"><img src="public/qr_github.png" alt="QR marimo on GitHub" style="height:84px;display:block;margin:0 auto;"><span class="cap" style="margin:0.1rem 0 0;">github:marimo-team</span></div>
190+
</div>
191+
</div>
192+
<div style="display:flex;align-items:center;gap:0.9rem;margin-top:0.7rem;"><img src="public/numfocus.png" alt="NumFOCUS affiliated project" style="height:40px;display:inline-block;flex:0 0 auto;"><span class="cap" style="margin:0;">marimo is licensed under Apache&nbsp;2.0 and free to use · marimo is a NumFOCUS-affiliated project.</span></div>

0 commit comments

Comments
 (0)