Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .claude/board/EPIPHANIES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
## 2026-07-08 — E-OCR-PARALLEL-1 — page-chunk rayon closes the throughput gap losslessly: 3.34× on 4 cores, byte-identical to serial (the operator-pinned topology, not whole-doc lanes / not a global crop pool)
**Status:** FINDING (measured; tesseract-rs `ocr-pdf`, determinism-gated; PR forthcoming)

**The topology (operator-ratified, sharpened from "lane per document" → "lane per document-CHUNK, mostly page"):** rayon over PAGE-CHUNK jobs — `Job = (doc_id, page_no)` + owned grey buffer. NOT whole-document lanes (a 200-page PDF would re-serialize on one thread — "ein Wal im Gartenteich") and NOT a global line/crop bucket pool (that reintroduces the serial gather/bucket/scatter phase + `(doc,line)` reassembly tags — "Tag-Konfetti"). Reassembly = `Vec<PageResult>` then `sort_by (doc_id, page_no)`: clean ownership, ordered, cheap. `OcrPipeline::ocr_pages_parallel` (feature `parallel`, default-on, rayon optional dep).

**Two iron invariants, both enforced not asserted:**
- **NO NESTED PARALLELISM.** Outer rayon owns all CPU parallelism; the inner forward (ndarray int8 VNNI GEMM) stays single-threaded BY DESIGN — documented as an invariant in `parallel.rs`. Verified zero `Rc`/`RefCell`/`UnsafeCell`/`par_iter` in the recognizer→network→core chain, so `&OcrPipeline` is `Sync` and task-sharing is compile-safe (no `unsafe impl Send/Sync` anywhere). The classic nested-rayon thread-salat is structurally avoided.
- **DETERMINISM = byte-identical to serial.** Parallel output must equal the serial path exactly (each line seeds its own `TRand` from `sample_iteration`; `&self` compute; no shared mutable state). The `parallel_determinism` test (2/2: per-page byte equality + out-of-order-jobs-resort) and `par_bench`'s per-run self-check are the HARD gate. Nothing about the math changes — only scheduling.

**Measured (`par_bench`, 10 committed pages, release, this 4-core box):** 1 thread **0.99×** (self-check ≈ serial), 2 threads **1.94×** (97% eff), 4 threads **3.34×** (84% eff) — near-linear, matching the pre-registered ~3.3–3.8× prediction. Determinism self-check green at every thread count (30/30, 10/10). This turns ~15 lines/s into ~50, closing the matched H2H line-gap (E-OCR-H2H-1) from **7.2× to ~2×** on 4 cores. The remainder is per-core throughput — width-bucketed batch-forward to fill VNNI/AMX tiles (`ndarray simd_soa`/"gridlake") — a LATER, **probe-gated** lever (truth-architect: only build it once benchmarks show underfilled tiles are the residual bottleneck).

**Where Morton/2-bit/cascade do NOT apply (asked this session, ruled out with reasons):** those are SEARCH/codec superpowers (CAM-PQ/palette/bgz-tensor at 611M lookups/s + 95% skip) — the dense int8 LSTM forward has no candidate set to prune, tiny cache-resident matrices, and a VNNI layout Morton would fight; 2-bit breaks byte-parity. The forward's only lawful levers are parallelism (this) + SoA-batched tile-fill (gridlake, probe-gated). Branch `claude/happy-hamilton-0azlw4`.

## 2026-07-08 — E-OCR-H2H-1 — the defensible head-to-head: recognition byte-parity confirmed on real photographed lines, the quality gap is INPUT CONDITIONING not the core, the matched speed gap is 7.3× (single-thread forward) with 4.6× leaner RAM
**Status:** FINDING (measured over 2286 HierText line crops + 300-crop matched speed; harnesses committed tesseract-rs `a02cb96`, report `.claude/harvest/h2h-report.md`)

Expand Down