memoization: Profiling support keeps track of lookups and hits.
* guix/memoization.scm (<cache>): New record type. (define-lookup-procedure, define-update-procedure): New macros. (cache-ref, cacheq-ref, cache-set!, cacheq-set!): New procedures. (cached/mv, cachedq/mv, cached, cachedq): Use them instead of 'hash-ref' and 'hash-set!'. (%make-hash-table*): When 'profiled?' returns true, return a <cache> object. (define-cache-procedure): Adjust to show cache lookups and hits.
This commit is contained in:
parent
252c408377
commit
6c80641d54
1 changed files with 69 additions and 24 deletions
|
@ -20,10 +20,48 @@
|
||||||
#:use-module (guix profiling)
|
#:use-module (guix profiling)
|
||||||
#:use-module (ice-9 match)
|
#:use-module (ice-9 match)
|
||||||
#:autoload (srfi srfi-1) (count)
|
#:autoload (srfi srfi-1) (count)
|
||||||
|
#:use-module (srfi srfi-9)
|
||||||
#:export (memoize
|
#:export (memoize
|
||||||
mlambda
|
mlambda
|
||||||
mlambdaq))
|
mlambdaq))
|
||||||
|
|
||||||
|
;; Data type representation a memoization cache when profiling is on.
|
||||||
|
(define-record-type <cache>
|
||||||
|
(make-cache table lookups hits)
|
||||||
|
cache?
|
||||||
|
(table cache-table)
|
||||||
|
(lookups cache-lookups set-cache-lookups!)
|
||||||
|
(hits cache-hits set-cache-hits!))
|
||||||
|
|
||||||
|
(define-syntax-rule (define-lookup-procedure proc get)
|
||||||
|
"Define a lookup procedure PROC. When profiling is turned off, PROC is set
|
||||||
|
to GET; when profiling is on, PROC is a wrapper around GET that keeps tracks
|
||||||
|
of lookups and cache hits."
|
||||||
|
(define proc
|
||||||
|
(if (profiled? "memoization")
|
||||||
|
(lambda (cache key default)
|
||||||
|
(let ((result (get (cache-table cache) key default)))
|
||||||
|
(set-cache-lookups! cache (+ 1 (cache-lookups cache)))
|
||||||
|
(unless (eq? result default)
|
||||||
|
(set-cache-hits! cache (+ 1 (cache-hits cache))))
|
||||||
|
result))
|
||||||
|
get)))
|
||||||
|
|
||||||
|
(define-syntax-rule (define-update-procedure proc put!)
|
||||||
|
"Define an update procedure PROC. When profiling is turned off, PROC is
|
||||||
|
equal to PUT!; when profiling is on, PROC is a wrapper around PUT and unboxes
|
||||||
|
the underlying hash table."
|
||||||
|
(define proc
|
||||||
|
(if (profiled? "memoization")
|
||||||
|
(lambda (cache key value)
|
||||||
|
(put! (cache-table cache) key value))
|
||||||
|
put!)))
|
||||||
|
|
||||||
|
(define-lookup-procedure cache-ref hash-ref)
|
||||||
|
(define-lookup-procedure cacheq-ref hashq-ref)
|
||||||
|
(define-update-procedure cache-set! hash-set!)
|
||||||
|
(define-update-procedure cacheq-set! hashq-set!)
|
||||||
|
|
||||||
(define-syntax-rule (call/mv thunk)
|
(define-syntax-rule (call/mv thunk)
|
||||||
(call-with-values thunk list))
|
(call-with-values thunk list))
|
||||||
(define-syntax-rule (return/mv lst)
|
(define-syntax-rule (return/mv lst)
|
||||||
|
@ -56,22 +94,24 @@ already-cached result."
|
||||||
(define-cache-procedure name hash-ref hash-set!
|
(define-cache-procedure name hash-ref hash-set!
|
||||||
call/mv return/mv))))
|
call/mv return/mv))))
|
||||||
|
|
||||||
(define-cache-procedure cached/mv hash-ref hash-set!)
|
(define-cache-procedure cached/mv cache-ref cache-set!)
|
||||||
(define-cache-procedure cachedq/mv hashq-ref hashq-set!)
|
(define-cache-procedure cachedq/mv cacheq-ref cacheq-set!)
|
||||||
(define-cache-procedure cached hash-ref hash-set! call/1 return/1)
|
(define-cache-procedure cached cache-ref cache-set! call/1 return/1)
|
||||||
(define-cache-procedure cachedq hashq-ref hashq-set! call/1 return/1)
|
(define-cache-procedure cachedq cacheq-ref cacheq-set! call/1 return/1)
|
||||||
|
|
||||||
(define %memoization-tables
|
(define %memoization-tables
|
||||||
;; Map procedures to the underlying hash table.
|
;; Map procedures to the underlying hash table.
|
||||||
(make-weak-key-hash-table))
|
(make-weak-key-hash-table))
|
||||||
|
|
||||||
(define %make-hash-table*
|
(define %make-hash-table*
|
||||||
|
;; When profiling is off, this is equivalent to 'make-hash-table'. When
|
||||||
|
;; profiling is on, return a hash table wrapped in a <cache> object.
|
||||||
(if (profiled? "memoization")
|
(if (profiled? "memoization")
|
||||||
(lambda (proc location)
|
(lambda (proc location)
|
||||||
(let ((table (make-hash-table)))
|
(let ((cache (make-cache (make-hash-table) 0 0)))
|
||||||
(hashq-set! %memoization-tables proc
|
(hashq-set! %memoization-tables proc
|
||||||
(cons table location))
|
(cons cache location))
|
||||||
table))
|
cache))
|
||||||
(lambda (proc location)
|
(lambda (proc location)
|
||||||
(make-hash-table))))
|
(make-hash-table))))
|
||||||
|
|
||||||
|
@ -80,35 +120,40 @@ already-cached result."
|
||||||
|
|
||||||
(define* (show-memoization-tables #:optional (port (current-error-port)))
|
(define* (show-memoization-tables #:optional (port (current-error-port)))
|
||||||
"Display to PORT statistics about the memoization tables."
|
"Display to PORT statistics about the memoization tables."
|
||||||
(define (table<? p1 p2)
|
(define (cache<? p1 p2)
|
||||||
(match p1
|
(match p1
|
||||||
((table1 . _)
|
((cache1 . _)
|
||||||
(match p2
|
(match p2
|
||||||
((table2 . _)
|
((cache2 . _)
|
||||||
(< (hash-count (const #t) table1)
|
(< (hash-count (const #t) (cache-table cache1))
|
||||||
(hash-count (const #t) table2)))))))
|
(hash-count (const #t) (cache-table cache2))))))))
|
||||||
|
|
||||||
(define tables
|
(define caches
|
||||||
(hash-map->list (lambda (key value)
|
(hash-map->list (lambda (key value)
|
||||||
value)
|
value)
|
||||||
%memoization-tables))
|
%memoization-tables))
|
||||||
|
|
||||||
(match (sort tables (negate table<?))
|
(match (sort caches (negate cache<?))
|
||||||
(((tables . locations) ...)
|
(((caches . locations) ...)
|
||||||
(format port "Memoization: ~a tables, ~a non-empty~%"
|
(format port "Memoization: ~a tables, ~a non-empty~%"
|
||||||
(length tables)
|
(length caches)
|
||||||
(count (lambda (table)
|
(count (lambda (cache)
|
||||||
(> (hash-count (const #t) table) 0))
|
(> (hash-count (const #t) (cache-table cache)) 0))
|
||||||
tables))
|
caches))
|
||||||
(for-each (lambda (table location)
|
(for-each (lambda (cache location)
|
||||||
(let ((size (hash-count (const #t) table)))
|
(let ((size (hash-count (const #t) (cache-table cache))))
|
||||||
(unless (zero? size)
|
(unless (zero? size)
|
||||||
(format port " ~a:~a:~a: \t~a entries~%"
|
(format port " ~a:~a:~a: \t~a entries, ~a lookups, ~a% hits~%"
|
||||||
(assq-ref location 'filename)
|
(assq-ref location 'filename)
|
||||||
(and=> (assq-ref location 'line) 1+)
|
(and=> (assq-ref location 'line) 1+)
|
||||||
(assq-ref location 'column)
|
(assq-ref location 'column)
|
||||||
size))))
|
size
|
||||||
tables locations))))
|
(cache-lookups cache)
|
||||||
|
(inexact->exact
|
||||||
|
(round
|
||||||
|
(* 100. (/ (cache-hits cache)
|
||||||
|
(cache-lookups cache) 1.))))))))
|
||||||
|
caches locations))))
|
||||||
|
|
||||||
(register-profiling-hook! "memoization" show-memoization-tables)
|
(register-profiling-hook! "memoization" show-memoization-tables)
|
||||||
|
|
||||||
|
|
Reference in a new issue