core: seq walks over concrete collections are linear (bench TOTAL -18%)

Reviewing flatiron's morsel batching for applicability turned up something
better hiding under the lazy machinery: every cell over a concrete
collection was produced by slicing the REMAINDER ((tuple/slice c 1) per
element in coll->cells, and rest/next sliced the same way), so any full walk
was O(n^2). mapv over 40k elements took 10.4 SECONDS; a 20k-element
first/rest loop took two.

Cells over indexed collections now walk by INDEX (one shared indexed-cells
helper, O(1) per step), and rest/next of a vector/tuple/list return an O(1)
lazy view from index 1 — which also makes (rest [1 2 3]) a SEQ, as in
Clojure (it was a vector-typed slice; seq?/vector? rows pin the change).

mapv 40k: 10405 -> 182 ms. rest-loop 20k: 2040 -> 31 ms. Whole bench:
seq-pipe -28%, into-vec -24%, str-join -18%, hof -26%, TOTAL 4565 -> 3753
(-18% vs main, back to back). Chunked seqs (jolt-yqc) drop in priority:
the quadratic walks were the actual cost; chunking now only amortizes
per-element closure allocation.

Nine regression rows incl. 20k/50k linear-scaling smoke tests. Gate exit 0.
This commit is contained in:
Yogthos 2026-06-10 18:56:16 -04:00
parent c24c7617b8
commit 4f78f6be33
2 changed files with 38 additions and 6 deletions

View file

@ -273,3 +273,20 @@
["splitv-at" "[[1 2] [3 4]]" "(splitv-at 2 [1 2 3 4])"]
["splitv-at first is vector" "true" "(vector? (first (splitv-at 2 [1 2 3])))"]
["splitv-at past end" "[[1 2] []]" "(splitv-at 5 [1 2])"])
# Linear seq walks (flatiron review find): cell chains over concrete
# collections walk by INDEX. Slicing the remainder per step made every full
# walk O(n^2) — mapv over 40k elements took ten seconds, a 20k first/rest
# loop took two. rest/next of an indexed collection is an O(1) lazy view —
# and therefore a SEQ, as in Clojure (it was a vector-typed slice).
(defspec "sequences / linear walks over concrete collections"
["rest of vector is a seq" "true" "(seq? (rest [1 2 3]))"]
["rest of vector not vector" "false" "(vector? (rest [1 2 3]))"]
["rest values" "(quote (2 3))" "(rest [1 2 3])"]
["next of vector" "(quote (2 3))" "(next [1 2 3])"]
["next exhausts to nil" "nil" "(next [1])"]
["rest exhausts to ()" "()" "(rest [1])"]
["rest-loop scales (20k linear)" "20000"
"(loop [xs (seq (vec (range 20000))) n 0] (if xs (recur (next xs) (inc n)) n))"]
["mapv scales (50k linear)" "50000" "(count (mapv inc (vec (range 50000))))"]
["nested walk" "[2 3]" "(vec (rest (mapv inc [0 1 2])))"])