; lib/artdag/stats.sx — observability over an execution: cache hit ratio and the
; compute work saved by memoization (weighted by the cost model). An exec is the
; {:results :recomputed :hits} record returned by artdag/execute. Depends on
; execute.sx (exec accessors) and cost.sx (artdag/-node-cost).

(define
  artdag/exec-total
  (fn (exec) (+ (artdag/recompute-count exec) (artdag/hit-count exec))))

; fraction of executed nodes served from cache (0 when nothing ran).
(define
  artdag/hit-ratio
  (fn
    (exec)
    (let
      ((n (artdag/exec-total exec)))
      (if (= n 0) 0 (/ (artdag/hit-count exec) n)))))

(define
  artdag/-sum-cost
  (fn
    (dag cost-fn ids)
    (reduce
      (fn (s id) (+ s (artdag/-node-cost dag cost-fn id)))
      0
      ids)))

; weighted compute work that actually ran this execution.
(define
  artdag/work-recomputed
  (fn
    (exec dag cost-fn)
    (artdag/-sum-cost dag cost-fn (get exec :recomputed))))

; weighted compute work avoided by cache hits.
(define
  artdag/work-saved
  (fn (exec dag cost-fn) (artdag/-sum-cost dag cost-fn (get exec :hits))))

; fraction of total weighted work that the cache saved (0 when no work at all).
(define
  artdag/savings-ratio
  (fn
    (exec dag cost-fn)
    (let
      ((saved (artdag/work-saved exec dag cost-fn))
        (ran (artdag/work-recomputed exec dag cost-fn)))
      (if (= (+ saved ran) 0) 0 (/ saved (+ saved ran))))))

; compact summary dict for logging.
(define artdag/exec-summary (fn (exec dag cost-fn) {:work-saved (artdag/work-saved exec dag cost-fn) :recomputed (artdag/recompute-count exec) :total (artdag/exec-total exec) :work-ran (artdag/work-recomputed exec dag cost-fn) :hits (artdag/hit-count exec)}))
