Archived
1
0
Fork 0

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:
Ludovic Courtès 2017-12-12 14:52:17 +01:00 committed by Ludovic Courtès
parent 252c408377
commit 6c80641d54
No known key found for this signature in database
GPG key ID: 090B11993D9AEBB5

View file

@ -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)