real chunked seqs for vector seqs

A vector's seq is now a real chunked-seq (chunked-seq? true), matching Clojure/
CLJS. Each vector-seq cell carries its backing vector + element index as two
cseq fields (cvec/ci, no extra allocation vs the old lazy cell), so:
  - chunk-first hands out a 32-element block (a pvec slice), chunk-rest is the
    seq at the next block boundary — the ChunkedSeq contract (chunk-first ++
    chunk-rest == the seq);
  - reduce/transduce take a fast path that walks the backing vector by index in
    a tight loop with no per-element seq cells (reduce over a 1M-vector ~0.4s).
The seq cell stays a cseq, so first/rest/count/printing and the ~26 cseq?
dispatch sites are untouched. The eager chunk-buffer model (chunk-buffer/chunk/
chunk-cons) is preserved for the round-trip case. No seed change (runtime only).
This commit is contained in:
Yogthos 2026-06-22 00:21:05 -04:00
parent 0e4ccc97e0
commit 3721423a64
4 changed files with 56 additions and 14 deletions

View file

@ -24,10 +24,18 @@
;; since one record type backs both (clojure.core/list? — jolt-75sv). The marker
;; lives on the cell, so (rest a-list) / (seq a-vector) / (map …) yield plain seq
;; cells and are not list?.
(define-record-type cseq (fields head (mutable tail) (mutable forced?) list?) (nongenerative chez-cseq-v2))
(define (cseq-realized head tail) (make-cseq head tail #t #f)) ; tail already a seq
(define (cseq-lazy head tail-thunk) (make-cseq head tail-thunk #f #f))
(define (cseq-list head tail) (make-cseq head tail #t #t)) ; a PersistentList node
;; cvec/ci: for a vector-backed seq cell, the backing vector and this cell's
;; element index — so it is a real chunked-seq (chunked-seq? true, chunk-first
;; hands out a 32-element block, chunk-rest is the seq at the next block) and
;; reduce iterates the vector directly with no per-element cells (jolt-hs5q).
;; cvec is #f for every other seq; stored as two fields (not a cons) so a vector
;; seq cell costs no extra allocation. The rest of the seq layer ignores them, so
;; first/rest/count/printing are unchanged.
(define-record-type cseq (fields head (mutable tail) (mutable forced?) list? cvec ci) (nongenerative chez-cseq-v3))
(define (cseq-realized head tail) (make-cseq head tail #t #f #f 0)) ; tail already a seq
(define (cseq-lazy head tail-thunk) (make-cseq head tail-thunk #f #f #f 0))
(define (cseq-list head tail) (make-cseq head tail #t #t #f 0)) ; a PersistentList node
(define (cseq-vec head tail-thunk v i) (make-cseq head tail-thunk #f #f v i)) ; vector-backed
(define (seq-first s) (cseq-head s))
(define (seq-more s) ; force the tail; returns a seq (cseq | jolt-nil)
(if (cseq-forced? s) (cseq-tail s)
@ -47,9 +55,9 @@
;; ============================================================================
(define (list->cseq xs) ; Scheme list -> realized cseq chain (jolt-nil if empty)
(if (null? xs) jolt-nil (cseq-realized (car xs) (list->cseq (cdr xs)))))
(define (vec->seq v i) ; lazy index seq over a persistent vector
(define (vec->seq v i) ; chunked index seq over a persistent vector
(if (fx>=? i (pvec-count v)) jolt-nil
(cseq-lazy (pvec-nth-d v i jolt-nil) (lambda () (vec->seq v (fx+ i 1))))))
(cseq-vec (pvec-nth-d v i jolt-nil) (lambda () (vec->seq v (fx+ i 1))) v i)))
(define (str->seq s i)
(if (fx>=? i (string-length s)) jolt-nil
(cseq-lazy (string-ref s i) (lambda () (str->seq s (fx+ i 1))))))
@ -168,10 +176,20 @@
;; honors `reduced`: a reducing fn that returns (reduced x) stops the fold and
;; unwraps to x (so does a reduced INIT). Checked at entry, so the value returned
;; by the last step is unwrapped on the next turn before the seq is consulted.
;; reduce a vector's backing store directly by index from element i — no per-
;; element seq cells. Honors `reduced`. The chunked-seq fast path.
(define (vec-reduce f acc v i)
(let ((n (pvec-count v)) (raw (pvec-v v)))
(let loop ((i i) (acc acc))
(cond ((jolt-reduced? acc) (jolt-reduced-val acc))
((fx>=? i n) acc)
(else (loop (fx+ i 1) (jolt-invoke f acc (vector-ref raw i))))))))
(define (reduce-seq f acc s)
(cond
((jolt-reduced? acc) (jolt-reduced-val acc))
((jolt-nil? s) acc)
;; a vector-backed (chunked) seq reduces its vector directly, in a tight loop.
((and (cseq? s) (cseq-cvec s)) (vec-reduce f acc (cseq-cvec s) (cseq-ci s)))
(else (reduce-seq f (jolt-invoke f acc (seq-first s)) (jolt-seq (seq-more s))))))
(define jolt-reduce
(case-lambda