Out-of-core canonical columns (disk-backed mmap store)#155
Conversation
Realize the "mmap (native)" canonical-store row of the memory model (§27): a canonical column may be backed by a disk np.memmap instead of RAM, so resident memory stays screen-bounded (the density pyramid), not data-bounded. Because a memmap is a transparent ndarray — same dedup key, same raw buffer pointer to the ctypes kernels — the store, zone maps (§22), bin_2d, the density pyramid (§5/§28), and drill-in consume it unchanged; the OS pages the file in on demand and evicts clean pages under pressure. The one new operation is building a column too large for RAM: xy._ooc.MemmapF64Builder streams canonical f64 to disk one batch at a time (peak RAM = one batch); open_f64 reopens a column from disk. memory_report() now separates canonical_bytes (RAM-resident) from canonical_mapped_bytes (disk-backed, reclaimable); an all-RAM figure reports mapped=0 unchanged. Spec: dossier §2 (out-of-core target realized), §27 (new rule 5), and lod-architecture §4.4. Tests: tests/test_ooc.py.
Merging this PR will not alter performance
Comparing Footnotes
|
Greptile SummaryThis PR adds disk-backed canonical columns for out-of-core native rendering. The main changes are:
Confidence Score: 4/5The empty-column persistence path needs a fix before merging. Non-empty construction, growth, ingestion, and memory classification are consistent. An empty finalized column reopens with one row. python/xy/_ooc.py
What T-Rex did
Important Files Changed
Reviews (1): Last reviewed commit: "Out-of-core canonical columns (disk-back..." | Re-trigger Greptile |
| # An empty column still needs a valid (zero-length) mapping; keep a | ||
| # single f64 slot on disk so the memmap open succeeds, view is [:0]. | ||
| with open(self.path, "r+b") as f: | ||
| f.truncate(_F64_BYTES) | ||
| return np.memmap(self.path, dtype=np.float64, mode="r", shape=(0,)) |
There was a problem hiding this comment.
Empty Column Reopens With One Row
When an empty builder is finalized, this branch stores one physical f64 slot so NumPy can create the mapping. open_f64() later infers the row count from the file size, so the same file reopens as a one-element column and introduces a bogus row.
Artifacts
Repro: minimal executable empty-column reopen harness
- Contains supporting evidence from the run (text/x-python; charset=utf-8).
Repro: runtime output showing the finalized-zero versus reopened-one-row discrepancy
- Keeps the command output available without making the summary code-heavy.
What
Realizes the "mmap (native)" canonical-store row of the memory model (dossier §27): a canonical column may be backed by a disk
np.memmapinstead of RAM, so a figure's resident memory stays screen-bounded (the density pyramid + grids), never data-bounded. This lets a scatter's canonicalx/yexceed RAM — the target §2 has always listed as "100M+ / out-of-core" but never realized on native.How
The load-bearing observation: a NumPy
memmapis a transparentndarray. A column backed by one:ColumnStorededup key (id(base),data_ptr,nbytes),arr.ctypes.data), so the OS pages it in on demand —zone_maps,bin_2d,range_indices, and the density pyramid all consume it with no special-casing,So the store, zone maps, and every LOD tier already work out-of-core. The one operation that genuinely needs a new path is creating an array too large for RAM:
xy._ooc.MemmapF64Builderstreams canonical f64 to a disk memmap one batch at a time (peak RAM = one batch);open_f64reopens a column from disk.fig.scatter(x=col, y=col, density=True)— and the whole pipeline runs against disk-backed truth.Honest accounting (§27)
memory_report()now separates:canonical_bytes— RAM-resident canonical (unchanged for all-RAM figures)canonical_mapped_bytes— disk-backed, OS-paged, reclaimableAn all-RAM figure reports
canonical_mapped_bytes == 0, so nothing changes for existing usage.Tests
tests/test_ooc.py(6 tests): builder round-trip + growth, empty builder, ingest-without-RAM-copy, in-RAM report unchanged, zone-maps match RAM, and screen-bounded density first-paint over a memmap-backed scatter. Full suite green; ruff / ty (new files) / pre-commit clean.Spec
lod-architecture.md§4.4 — canonical out-of-core landed