;; Phase 0c — persistent-collection perf experiment. ;; ;; Decides shim-vs-self-hosted for collections: is a persistent HAMT fast enough ;; on the Chez substrate that we can afford to SELF-HOST it (in Clojure compiled ;; by Chez) rather than keep it in the Scheme shim? This measures the substrate ;; ceiling with a hand-written Scheme HAMT (what the backend would emit) against ;; Chez's native mutable hashtable (the non-persistent lower bound), on the ;; collections-bench map workload (freq-map + sum-vals, n keys mod 4096). ;; chez --script collections-experiment.ss [n=30000] [optlevel=2] (import (chezscheme)) (optimize-level (let ((a (command-line-arguments))) (if (and (pair? a) (pair? (cdr a))) (string->number (cadr a)) 2))) ;; ---- persistent bitmap HAMT (assoc/get), 5 bits/level, integer-key hash ------ (define-record-type hnode (fields bitmap arr) (nongenerative hnode-v1)) (define empty-map (make-hnode 0 (vector))) (define (popcount n) (let loop ((n n) (c 0)) (if (fx=? n 0) c (loop (fxand n (fx- n 1)) (fx+ c 1))))) (define (mask h shift) (fxand (fxsra h shift) 31)) (define (idxof bitmap bit) (popcount (fxand bitmap (fx- bit 1)))) (define (vec-insert v i x) (let* ((n (vector-length v)) (out (make-vector (fx+ n 1)))) (let loop ((j 0)) (when (fx? shift 30) ;; hash exhausted (won't happen for distinct small ints) — chain via assoc-list leaf (cons 'collision (list e1 (cons k2 v2))) (let ((i1 (mask k1h shift)) (i2 (mask k2h shift))) (if (fx=? i1 i2) (let ((sub (merge-leaves (fx+ shift 5) k1h e1 k2h k2 v2))) (make-hnode (fxsll 1 i1) (vector sub))) (let* ((b1 (fxsll 1 i1)) (b2 (fxsll 1 i2))) (if (fx replace (make-hnode bm (vec-set arr i (cons key val)))) (else ; leaf, diff key -> split (make-hnode bm (vec-set arr i (merge-leaves (fx+ shift 5) (car child) child h key val))))))))) (define (assoc-map m key val) (assoc-h m 0 key key val)) ; hash = key (distinct small ints) (define (get-h node shift h key default) (let* ((bit (fxsll 1 (mask h shift))) (bm (hnode-bitmap node))) (if (fx=? 0 (fxand bm bit)) default (let ((child (vector-ref (hnode-arr node) (idxof bm bit)))) (cond ((hnode? child) (get-h child (fx+ shift 5) h key default)) ((eqv? (car child) key) (cdr child)) (else default)))))) (define (get-map m key default) (get-h m 0 key key default)) ;; ---- workloads (mirror bench/collections.clj freq-map + sum-vals) ------------ (define buckets 4096) (define (freq-hamt n) (let loop ((i 0) (m empty-map)) (if (fxinexact (/ (apply + acc) 3.0)))))) (let* ((a (command-line-arguments)) (n (if (pair? a) (string->number (car a)) 30000))) (printf "collections map-churn (n=~a, ~a buckets)\n" n buckets) (bench "persistent HAMT (self-hostable) " freq-hamt sum-hamt n) (bench "native hashtable (mutable, ceil)" freq-native sum-native n))