run-benchmark.scm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. #!/bin/sh
  2. # -*- Scheme -*-
  3. exec ${GUILE-guile} -q -l "$0" \
  4. -c '(apply main (cdr (command-line)))' \
  5. --benchmark-dir="$(dirname $0)" "$@"
  6. !#
  7. ;;; Copyright (C) 2008, 2009 Free Software Foundation, Inc.
  8. ;;;
  9. ;;; This program is free software; you can redistribute it and/or
  10. ;;; modify it under the terms of the GNU Lesser General Public License
  11. ;;; as published by the Free Software Foundation; either version 3, or
  12. ;;; (at your option) any later version.
  13. ;;;
  14. ;;; This program is distributed in the hope that it will be useful,
  15. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ;;; GNU Lesser General Public License for more details.
  18. ;;;
  19. ;;; You should have received a copy of the GNU Lesser General Public
  20. ;;; License along with this software; see the file COPYING.LESSER. If
  21. ;;; not, write to the Free Software Foundation, Inc., 51 Franklin
  22. ;;; Street, Fifth Floor, Boston, MA 02110-1301 USA
  23. (use-modules (ice-9 rdelim)
  24. (ice-9 popen)
  25. (ice-9 regex)
  26. (ice-9 format)
  27. (ice-9 pretty-print)
  28. (srfi srfi-1)
  29. (srfi srfi-37))
  30. ;;;
  31. ;;; Running Guile.
  32. ;;;
  33. (define (run-reference-guile env bench-dir profile-opts bench)
  34. "Run the ``mainstream'' Guile, i.e., Guile 1.9 with its own GC."
  35. (open-input-pipe (string-append
  36. env " "
  37. bench-dir "/gc-profile.scm " profile-opts
  38. " \"" bench "\"")))
  39. (define (run-bdwgc-guile env bench-dir profile-opts options bench)
  40. "Run the Guile port to the Boehm-Demers-Weiser GC (BDW-GC)."
  41. (let ((fsd (assoc-ref options 'free-space-divisor)))
  42. (open-input-pipe (string-append env " "
  43. "GC_FREE_SPACE_DIVISOR="
  44. (number->string fsd)
  45. (if (or (assoc-ref options 'incremental?)
  46. (assoc-ref options 'generational?))
  47. " GC_ENABLE_INCREMENTAL=yes"
  48. "")
  49. (if (assoc-ref options 'generational?)
  50. " GC_PAUSE_TIME_TARGET=999999"
  51. "")
  52. (if (assoc-ref options 'parallel?)
  53. "" ;; let it choose the number of procs
  54. " GC_MARKERS=1")
  55. " "
  56. bench-dir "/gc-profile.scm " profile-opts
  57. " \"" bench "\""))))
  58. ;;;
  59. ;;; Extracting performance results.
  60. ;;;
  61. (define (grep regexp input)
  62. "Read line by line from the @var{input} port and return all matches for
  63. @var{regexp}."
  64. (let ((regexp (if (string? regexp) (make-regexp regexp) regexp)))
  65. (with-input-from-port input
  66. (lambda ()
  67. (let loop ((line (read-line))
  68. (result '()))
  69. (format #t "> ~A~%" line)
  70. (if (eof-object? line)
  71. (reverse result)
  72. (cond ((regexp-exec regexp line)
  73. =>
  74. (lambda (match)
  75. (loop (read-line)
  76. (cons match result))))
  77. (else
  78. (loop (read-line) result)))))))))
  79. (define (parse-result benchmark-output)
  80. (let ((result (grep "^(execution time|heap size):[[:blank:]]+([0-9.]+)"
  81. benchmark-output)))
  82. (fold (lambda (match result)
  83. (cond ((equal? (match:substring match 1) "execution time")
  84. (cons (cons 'execution-time
  85. (string->number (match:substring match 2)))
  86. result))
  87. ((equal? (match:substring match 1) "heap size")
  88. (cons (cons 'heap-size
  89. (string->number (match:substring match 2)))
  90. result))
  91. (else
  92. result)))
  93. '()
  94. result)))
  95. (define (pretty-print-result benchmark reference bdwgc)
  96. (define ref-heap (assoc-ref reference 'heap-size))
  97. (define ref-time (assoc-ref reference 'execution-time))
  98. (define (distance x1 y1 x2 y2)
  99. ;; Return the distance between (X1,Y1) and (X2,Y2). Y is the heap size,
  100. ;; in MiB and X is the execution time in seconds.
  101. (let ((y1 (/ y1 (expt 2 20)))
  102. (y2 (/ y2 (expt 2 20))))
  103. (sqrt (+ (expt (- y1 y2) 2)
  104. (expt (- x1 x2) 2)))))
  105. (define (score time heap)
  106. ;; Return a score lower than +1.0. The score is positive if the
  107. ;; distance to the origin of (TIME,HEAP) is smaller than that of
  108. ;; (REF-TIME,REF-HEAP), negative otherwise.
  109. ;; heap ^ .
  110. ;; size | . worse
  111. ;; | . [-]
  112. ;; | .
  113. ;; | . . . .ref. . . .
  114. ;; | .
  115. ;; | [+] .
  116. ;; | better .
  117. ;; 0 +-------------------->
  118. ;; exec. time
  119. (let ((ref-dist (distance ref-time ref-heap 0 0))
  120. (dist (distance time heap 0 0)))
  121. (/ (- ref-dist dist) ref-dist)))
  122. (define (score-string time heap)
  123. ;; Return a string denoting a bar to illustrate the score of (TIME,HEAP)
  124. ;; relative to (REF-TIME,REF-HEAP).
  125. (define %max-width 15)
  126. (let ((s (score time heap)))
  127. (make-string (inexact->exact (round (* (if (< s 0.0) (- s) s)
  128. %max-width)))
  129. (if (< s 0.0)
  130. #\-
  131. #\+))))
  132. (define (print-line name result ref?)
  133. (let ((name (string-pad-right name 23))
  134. (time (assoc-ref result 'execution-time))
  135. (heap (assoc-ref result 'heap-size)))
  136. (format #t "~a ~6,2f (~,2fx) ~7,3f (~,2fx)~A~%"
  137. name
  138. (/ heap (expt 2.0 20)) (/ heap ref-heap 1.0)
  139. time (/ time ref-time 1.0)
  140. (if (not ref?)
  141. (string-append " "
  142. (score-string time heap))
  143. ""))))
  144. (format #t "benchmark: `~a'~%" benchmark)
  145. (format #t " heap size (MiB) execution time (s.)~%")
  146. (print-line "Guile" reference #t)
  147. (for-each (lambda (bdwgc)
  148. (let ((name (format #f "BDW-GC, FSD=~a~a"
  149. (assoc-ref bdwgc 'free-space-divisor)
  150. (cond ((assoc-ref bdwgc 'incremental?)
  151. " incr.")
  152. ((assoc-ref bdwgc 'generational?)
  153. " gene.")
  154. ((assoc-ref bdwgc 'parallel?)
  155. " paral.")
  156. (else "")))))
  157. (print-line name bdwgc #f)))
  158. bdwgc))
  159. (define (print-raw-result benchmark reference bdwgc)
  160. (pretty-print `(,benchmark
  161. (reference . ,reference)
  162. (bdw-gc . ,bdwgc))))
  163. ;;;
  164. ;;; Option processing.
  165. ;;;
  166. (define %options
  167. (list (option '(#\h "help") #f #f
  168. (lambda args
  169. (show-help)
  170. (exit 0)))
  171. (option '(#\r "reference") #t #f
  172. (lambda (opt name arg result)
  173. (alist-cons 'reference-environment arg
  174. (alist-delete 'reference-environment result
  175. eq?))))
  176. (option '(#\b "bdw-gc") #t #f
  177. (lambda (opt name arg result)
  178. (alist-cons 'bdwgc-environment arg
  179. (alist-delete 'bdwgc-environment result
  180. eq?))))
  181. (option '(#\d "benchmark-dir") #t #f
  182. (lambda (opt name arg result)
  183. (alist-cons 'benchmark-directory arg
  184. (alist-delete 'benchmark-directory result
  185. eq?))))
  186. (option '(#\p "profile-options") #t #f
  187. (lambda (opt name arg result)
  188. (let ((opts (assoc-ref result 'profile-options)))
  189. (alist-cons 'profile-options
  190. (string-append opts " " arg)
  191. (alist-delete 'profile-options result
  192. eq?)))))
  193. (option '(#\l "log-file") #t #f
  194. (lambda (opt name arg result)
  195. (alist-cons 'log-port (open-output-file arg)
  196. (alist-delete 'log-port result
  197. eq?))))
  198. (option '("raw") #f #f
  199. (lambda (opt name arg result)
  200. (alist-cons 'printer print-raw-result
  201. (alist-delete 'printer result eq?))))
  202. (option '("load-results") #f #f
  203. (lambda (opt name arg result)
  204. (alist-cons 'load-results? #t result)))))
  205. (define %default-options
  206. `((reference-environment . "GUILE=guile")
  207. (benchmark-directory . "./gc-benchmarks")
  208. (log-port . ,(current-output-port))
  209. (profile-options . "")
  210. (input . ())
  211. (printer . ,pretty-print-result)))
  212. (define (show-help)
  213. (format #t "Usage: run-benchmark [OPTIONS] BENCHMARKS...
  214. Run BENCHMARKS (a list of Scheme files) and display a performance
  215. comparison of standard Guile (1.9) and the BDW-GC-based Guile.
  216. -h, --help Show this help message
  217. -r, --reference=ENV
  218. -b, --bdw-gc=ENV
  219. Use ENV as the environment necessary to run the
  220. \"reference\" Guile (1.9) or the BDW-GC-based Guile,
  221. respectively. At a minimum, ENV should define the
  222. `GUILE' environment variable. For example:
  223. --reference='GUILE=/foo/bar/guile LD_LIBRARY_PATH=/foo'
  224. -p, --profile-options=OPTS
  225. Pass OPTS as additional options for `gc-profile.scm'.
  226. -l, --log-file=FILE
  227. Save output to FILE instead of the standard output.
  228. --raw Write benchmark results in raw (s-exp) format.
  229. --load-results
  230. Load raw (s-exp) results instead of actually running
  231. the benchmarks.
  232. -d, --benchmark-dir=DIR
  233. Use DIR as the GC benchmark directory where `gc-profile.scm'
  234. lives (it is automatically determined by default).
  235. Report bugs to <bug-guile@gnu.org>.~%"))
  236. (define (parse-args args)
  237. (define (leave fmt . args)
  238. (apply format (current-error-port) (string-append fmt "~%") args)
  239. (exit 1))
  240. (args-fold args %options
  241. (lambda (opt name arg result)
  242. (leave "~A: unrecognized option" opt))
  243. (lambda (file result)
  244. (let ((files (or (assoc-ref result 'input) '())))
  245. (alist-cons 'input (cons file files)
  246. (alist-delete 'input result eq?))))
  247. %default-options))
  248. ;;;
  249. ;;; The main program.
  250. ;;;
  251. (define (main . args)
  252. (let* ((args (parse-args args))
  253. (benchmark-files (assoc-ref args 'input)))
  254. (let* ((log (assoc-ref args 'log-port))
  255. (bench-dir (assoc-ref args 'benchmark-directory))
  256. (ref-env (assoc-ref args 'reference-environment))
  257. (bdwgc-env (or (assoc-ref args 'bdwgc-environment)
  258. (string-append "GUILE=" bench-dir
  259. "/../meta/guile")))
  260. (prof-opts (assoc-ref args 'profile-options))
  261. (print (assoc-ref args 'printer)))
  262. (define (run benchmark)
  263. (let ((ref (parse-result (run-reference-guile ref-env
  264. bench-dir
  265. prof-opts
  266. benchmark)))
  267. (bdwgc (map (lambda (fsd incremental?
  268. generational? parallel?)
  269. (let ((opts
  270. (list
  271. (cons 'free-space-divisor fsd)
  272. (cons 'incremental? incremental?)
  273. (cons 'generational? generational?)
  274. (cons 'parallel? parallel?))))
  275. (append opts
  276. (parse-result
  277. (run-bdwgc-guile bdwgc-env
  278. bench-dir
  279. prof-opts
  280. opts
  281. benchmark)))))
  282. '( 3 6 9 3 3)
  283. '(#f #f #f #t #f) ;; incremental
  284. '(#f #f #f #f #t) ;; generational
  285. '(#f #f #f #f #f)))) ;; parallel
  286. `(,benchmark
  287. (reference . ,ref)
  288. (bdw-gc . ,bdwgc))))
  289. (define (load-results file)
  290. (with-input-from-file file
  291. (lambda ()
  292. (let loop ((results '()) (o (read)))
  293. (if (eof-object? o)
  294. (reverse results)
  295. (loop (cons o results)
  296. (read)))))))
  297. (for-each (lambda (result)
  298. (let ((benchmark (car result))
  299. (ref (assoc-ref (cdr result) 'reference))
  300. (bdwgc (assoc-ref (cdr result) 'bdw-gc)))
  301. (with-output-to-port log
  302. (lambda ()
  303. (print benchmark ref bdwgc)
  304. (newline)
  305. (force-output)))))
  306. (if (assoc-ref args 'load-results?)
  307. (append-map load-results benchmark-files)
  308. (map run benchmark-files))))))