perf: lazy $0 rebuild via dirty flag (field assigns no longer re-join all NF fields)
ober
92de278a3a65bcd75b0144975e41fc4a29b57cf7
--- a/lib/jerboa-awk/runtime.sls +++ b/lib/jerboa-awk/runtime.sls @@ -222,6 +222,7 @@ (mutable rand-initialized? %awk-env-rand-initialized? %awk-env-rand-initialized?-set!) (mutable exit-code %awk-env-exit-code %awk-env-exit-code-set!) (mutable local-frames %awk-env-local-frames %awk-env-local-frames-set!) + (mutable record-dirty? %awk-env-record-dirty? %awk-env-record-dirty?-set!) (immutable owner-thread %awk-env-owner-thread))) ;; Preserve the original public constructor surface. The evaluator-owned @@ -235,6 +236,7 @@ (make-regex-cache) '() '() 1 0 #f exit-code local-frames + #f (get-thread-id))) (define (env-assert-owner! who env) @@ -404,7 +406,12 @@ (env-assert-owner! 'env-get-field env) (let ((fields (awk-env-fields env))) (if (and (>= idx 0) (< idx (vector-length fields))) - (vector-ref fields idx) + (begin + ;; $0 is rebuilt lazily: field assignments only mark the record dirty, + ;; so a loop like {for(i=1;i<=NF;i++) $i=...} rebuilds $0 once, on read. + (when (and (= idx 0) (%awk-env-record-dirty? env)) + (env-rebuild-record! env)) + (vector-ref (awk-env-fields env) idx)) (make-awk-string "")))) (define (env-set-field! env idx val) @@ -422,9 +429,9 @@ (vector-copy! old-fields 0 new-fields 0 (vector-length old-fields)) (awk-env-fields-set! env new-fields))) (vector-set! (awk-env-fields env) idx v) - ;; Rebuild $0 + ;; Mark $0 stale; it is rebuilt lazily on the next read of field 0. (when (> idx 0) - (env-rebuild-record! env)))) + (%awk-env-record-dirty?-set! env #t)))) (define (env-set-record! env str) (env-assert-owner! 'env-set-record! env) @@ -434,6 +441,7 @@ (map make-awk-strnum fields))))) (awk-env-fields-set! env vec) (awk-env-nf-set! env nf) + (%awk-env-record-dirty?-set! env #f) (hash-put! (awk-env-globals env) 'NF (make-awk-number nf))))) (define (env-rebuild-record! env) @@ -445,7 +453,8 @@ (loop (+ i 1) (cons (awk->string (env-get-field env i)) acc)))))) (vector-set! (awk-env-fields env) 0 - (make-awk-string (string-join parts ofs))))) + (make-awk-string (string-join parts ofs))) + (%awk-env-record-dirty?-set! env #f))) (define (env-set-nf! env new-nf) (when (> new-nf max-nf) @@ -455,7 +464,7 @@ ((= new-nf old-nf) (void)) ((< new-nf old-nf) (awk-env-nf-set! env new-nf) - (env-rebuild-record! env)) + (%awk-env-record-dirty?-set! env #t)) (else ;; Expand (let ((new-vec (make-vector (+ new-nf 1) (make-awk-string ""))) @@ -463,7 +472,7 @@ (vector-copy! old-fields 0 new-vec 0 (vector-length old-fields)) (awk-env-fields-set! env new-vec) (awk-env-nf-set! env new-nf) - (env-rebuild-record! env)))))) + (%awk-env-record-dirty?-set! env #t)))))) ;;; ---- Field splitting ----