(module benchmark mzscheme
(require
(lib "etc.ss")
(lib "comprehensions.ss" "srfi" "42")
(planet "plt/base.ss" ("schematics" "schemeunit.plt" 2))
(planet "test.ss" ("schematics" "schemeunit.plt" 2))
(planet "science.ss" ("williams" "science.plt" 2))
(file "benchmark-log.ss"))
(require-for-syntax
(lib "main-collects.ss" "setup"))
(provide
benchmark-case
benchmark-test-case-log-file
this-expression-benchmark-log-file
collect-and-time
check-faster
check-faster-times
faster?)
(define-struct (benchmark-test-case schemeunit-test-case) (log-file))
(define-syntax (this-expression-benchmark-log-file stx)
(syntax-case stx ()
[(this-expression-benchmark-log-file)
(with-syntax
([directory
(let* ([source (syntax-source stx)]
[source (and (path? source) source)]
[local (or (current-load-relative-directory) (current-directory))]
[dir (path->main-collects-relative
(or (and source (file-exists? source)
(let-values ([(base file dir?) (split-path source)])
(and (path? base)
(path->complete-path base local))))
local))])
(if (and (pair? dir) (eq? 'collects (car dir)))
(with-syntax ([d dir])
#'(main-collects-relative->path 'd))
(with-syntax ([d (if (bytes? dir) dir (path->bytes dir))])
#'(bytes->path d))))]
[file-name
(let* ([f (syntax-source stx)]
[f (and f (path? f) (file-exists? f)
(let-values ([(base file dir?) (split-path f)]) file))])
(if f
(with-syntax ([f (path->bytes f)]) #'(bytes->path f))
#'#f))])
(syntax/loc stx
(build-path
directory
(string-append
(path->string file-name)
".benchmark-log"))))]))
(define-syntax (benchmark-case stx)
(syntax-case stx ()
[(_ name expr ...)
(with-syntax ([log-file
(datum->syntax-object
stx
'(this-expression-benchmark-log-file)
stx)])
(syntax
(make-benchmark-test-case
name
(lambda ()
(let* ([test-thunk (lambda () expr ...)]
[times (collect-and-time test-thunk)]
[previous-run (find-most-recent-run log-file name)])
(if previous-run
(check-faster-times times (run-times previous-run)))
(add-run log-file name times)
#t))
log-file)))]))
(define (cpu-time thunk)
(let-values (([result cpu real gc] (time-apply thunk null)))
cpu))
(define (collect-and-time thunk)
(collect-garbage)
(vector-ec (:range i 0 10) (cpu-time thunk)))
(define (faster? thunk1 thunk2)
(let ([time1 (collect-and-time thunk1)]
[time2 (collect-and-time thunk2)])
(< (mean time1) (mean time2))))
(define-check (check-faster thunk1 thunk2)
(let ([times1 (collect-and-time thunk1)]
[times2 (collect-and-time thunk2)])
(check-faster-times times1 times2)))
(define (check-faster-times times1 times2)
(if (< (mean times1) (mean times2))
#t
(with-check-info
(('mean1 (mean times1))
('mean2 (mean times2))
('std-dev1 (standard-deviation times1))
('std-dev2 (standard-deviation times2))
('slowdown (/ (mean times1) (mean times2))))
(fail-check))))
)