mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-29 19:30:36 +02:00
Add GC benchmarks.
This commit is contained in:
parent
4a462e3544
commit
8da56ffc0b
5 changed files with 402 additions and 0 deletions
154
gc-benchmarks/gc-profile.scm
Executable file
154
gc-benchmarks/gc-profile.scm
Executable file
|
@ -0,0 +1,154 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# -*- Scheme -*-
|
||||||
|
exec ${GUILE-guile} --no-debug -q -l "$0" \
|
||||||
|
-c '(apply main (command-line))' "$@"
|
||||||
|
!#
|
||||||
|
;;; Copyright (C) 2008 Free Software Foundation, Inc.
|
||||||
|
;;;
|
||||||
|
;;; This program is free software; you can redistribute it and/or modify
|
||||||
|
;;; it under the terms of the GNU General Public License as published by
|
||||||
|
;;; the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
;;; any later version.
|
||||||
|
;;;
|
||||||
|
;;; This program is distributed in the hope that it will be useful,
|
||||||
|
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
;;; GNU General Public License for more details.
|
||||||
|
;;;
|
||||||
|
;;; You should have received a copy of the GNU General Public License
|
||||||
|
;;; along with this software; see the file COPYING. If not, write to
|
||||||
|
;;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
;;; Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
(use-modules (ice-9 format)
|
||||||
|
(ice-9 rdelim)
|
||||||
|
(ice-9 regex)
|
||||||
|
(srfi srfi-1))
|
||||||
|
|
||||||
|
(define (memory-mappings pid)
|
||||||
|
"Return an list of alists, each of which contains information about a
|
||||||
|
memory mapping of process @var{pid}. This information is obtained by reading
|
||||||
|
@file{/proc/PID/smaps} on Linux. See `procs(5)' for details."
|
||||||
|
|
||||||
|
(define mapping-line-rx
|
||||||
|
(make-regexp
|
||||||
|
"^([[:xdigit:]]+)-([[:xdigit:]]+) ([rwx-]{3}[ps]) ([[:xdigit:]]+) [0-9]{2}:[0-9]{2} [0-9]+[[:blank:]]+(.*)$"))
|
||||||
|
|
||||||
|
(define rss-line-rx
|
||||||
|
(make-regexp
|
||||||
|
"^Rss:[[:blank:]]+([[:digit:]]+) kB$"))
|
||||||
|
|
||||||
|
(with-input-from-port (open-input-file (format #f "/proc/~a/smaps" pid))
|
||||||
|
(lambda ()
|
||||||
|
(let loop ((line (read-line))
|
||||||
|
(result '()))
|
||||||
|
(if (eof-object? line)
|
||||||
|
(reverse result)
|
||||||
|
(cond ((regexp-exec mapping-line-rx line)
|
||||||
|
=>
|
||||||
|
(lambda (match)
|
||||||
|
(let ((mapping-start (string->number
|
||||||
|
(match:substring match 1)
|
||||||
|
16))
|
||||||
|
(mapping-end (string->number
|
||||||
|
(match:substring match 2)
|
||||||
|
16))
|
||||||
|
(access-bits (match:substring match 3))
|
||||||
|
(name (match:substring match 5)))
|
||||||
|
(loop (read-line)
|
||||||
|
(cons `((mapping-start . ,mapping-start)
|
||||||
|
(mapping-end . ,mapping-end)
|
||||||
|
(access-bits . ,access-bits)
|
||||||
|
(name . ,(if (string=? name "")
|
||||||
|
#f
|
||||||
|
name)))
|
||||||
|
result)))))
|
||||||
|
((regexp-exec rss-line-rx line)
|
||||||
|
=>
|
||||||
|
(lambda (match)
|
||||||
|
(let ((section+ (cons (cons 'rss
|
||||||
|
(string->number
|
||||||
|
(match:substring match 1)))
|
||||||
|
(car result))))
|
||||||
|
(loop (read-line)
|
||||||
|
(cons section+ (cdr result))))))
|
||||||
|
(else
|
||||||
|
(loop (read-line) result))))))))
|
||||||
|
|
||||||
|
(define (total-heap-size pid)
|
||||||
|
"Return the total heap size of process @var{pid}."
|
||||||
|
|
||||||
|
(define heap-or-anon-rx
|
||||||
|
(make-regexp "\\[(heap|anon)\\]"))
|
||||||
|
|
||||||
|
(define private-mapping-rx
|
||||||
|
(make-regexp "^[r-][w-][x-]p$"))
|
||||||
|
|
||||||
|
(fold (lambda (heap total+rss)
|
||||||
|
(let ((name (assoc-ref heap 'name))
|
||||||
|
(perm (assoc-ref heap 'access-bits)))
|
||||||
|
;; Include anonymous private mappings.
|
||||||
|
(if (or (and (not name)
|
||||||
|
(regexp-exec private-mapping-rx perm))
|
||||||
|
(and name
|
||||||
|
(regexp-exec heap-or-anon-rx name)))
|
||||||
|
(let ((start (assoc-ref heap 'mapping-start))
|
||||||
|
(end (assoc-ref heap 'mapping-end))
|
||||||
|
(rss (assoc-ref heap 'rss)))
|
||||||
|
(cons (+ (car total+rss) (- end start))
|
||||||
|
(+ (cdr total+rss) rss)))
|
||||||
|
total+rss)))
|
||||||
|
'(0 . 0)
|
||||||
|
(memory-mappings pid)))
|
||||||
|
|
||||||
|
|
||||||
|
(define (display-stats start end)
|
||||||
|
(define (->usecs sec+usecs)
|
||||||
|
(+ (* 1000000 (car sec+usecs))
|
||||||
|
(cdr sec+usecs)))
|
||||||
|
|
||||||
|
(let ((usecs (- (->usecs end) (->usecs start)))
|
||||||
|
(heap-size (total-heap-size (getpid)))
|
||||||
|
(gc-heap-size (assoc-ref (gc-stats) 'heap-size)))
|
||||||
|
|
||||||
|
(format #t "execution time: ~6,3f seconds~%"
|
||||||
|
(/ usecs 1000000.0))
|
||||||
|
|
||||||
|
(and gc-heap-size
|
||||||
|
(format #t "GC-reported heap size: ~8d B (~1,2f MiB)~%"
|
||||||
|
gc-heap-size
|
||||||
|
(/ gc-heap-size 1024.0 1024.0)))
|
||||||
|
|
||||||
|
(format #t "heap size: ~8d B (~1,2f MiB)~%"
|
||||||
|
(car heap-size)
|
||||||
|
(/ (car heap-size) 1024.0 1024.0))
|
||||||
|
(format #t "heap RSS: ~8d KiB (~1,2f MiB)~%"
|
||||||
|
(cdr heap-size)
|
||||||
|
(/ (cdr heap-size) 1024.0))
|
||||||
|
;; (system (format #f "cat /proc/~a/smaps" (getpid)))
|
||||||
|
;; (system (format #f "exmtool procs | grep -E '^(PID|~a)'" (getpid)))
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
(define (main . args)
|
||||||
|
(if (not (= (length args) 2))
|
||||||
|
(begin
|
||||||
|
(format #t "Usage: run FILE.SCM
|
||||||
|
|
||||||
|
Load FILE.SCM, a Guile Scheme source file, and report its execution time and
|
||||||
|
final heap usage.~%")
|
||||||
|
(exit 1)))
|
||||||
|
|
||||||
|
(let ((prog (cadr args))
|
||||||
|
(start (gettimeofday)))
|
||||||
|
(format #t "running `~a'...~%" prog)
|
||||||
|
(dynamic-wind
|
||||||
|
(lambda ()
|
||||||
|
#t)
|
||||||
|
(lambda ()
|
||||||
|
(set! quit (lambda args args))
|
||||||
|
(load prog))
|
||||||
|
(lambda ()
|
||||||
|
(let ((end (gettimeofday)))
|
||||||
|
(format #t "done~%")
|
||||||
|
(display-stats start end))))))
|
210
gc-benchmarks/gcbench.scm
Normal file
210
gc-benchmarks/gcbench.scm
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
; This is adapted from a benchmark written by John Ellis and Pete Kovac
|
||||||
|
; of Post Communications.
|
||||||
|
; It was modified by Hans Boehm of Silicon Graphics.
|
||||||
|
; It was translated into Scheme by William D Clinger of Northeastern Univ;
|
||||||
|
; the Scheme version uses (RUN-BENCHMARK <string> <thunk>)
|
||||||
|
; Last modified 30 May 1997.
|
||||||
|
;
|
||||||
|
; This is no substitute for real applications. No actual application
|
||||||
|
; is likely to behave in exactly this way. However, this benchmark was
|
||||||
|
; designed to be more representative of real applications than other
|
||||||
|
; Java GC benchmarks of which we are aware.
|
||||||
|
; It attempts to model those properties of allocation requests that
|
||||||
|
; are important to current GC techniques.
|
||||||
|
; It is designed to be used either to obtain a single overall performance
|
||||||
|
; number, or to give a more detailed estimate of how collector
|
||||||
|
; performance varies with object lifetimes. It prints the time
|
||||||
|
; required to allocate and collect balanced binary trees of various
|
||||||
|
; sizes. Smaller trees result in shorter object lifetimes. Each cycle
|
||||||
|
; allocates roughly the same amount of memory.
|
||||||
|
; Two data structures are kept around during the entire process, so
|
||||||
|
; that the measured performance is representative of applications
|
||||||
|
; that maintain some live in-memory data. One of these is a tree
|
||||||
|
; containing many pointers. The other is a large array containing
|
||||||
|
; double precision floating point numbers. Both should be of comparable
|
||||||
|
; size.
|
||||||
|
;
|
||||||
|
; The results are only really meaningful together with a specification
|
||||||
|
; of how much memory was used. It is possible to trade memory for
|
||||||
|
; better time performance. This benchmark should be run in a 32 MB
|
||||||
|
; heap, though we don't currently know how to enforce that uniformly.
|
||||||
|
|
||||||
|
; In the Java version, this routine prints the heap size and the amount
|
||||||
|
; of free memory. There is no portable way to do this in Scheme; each
|
||||||
|
; implementation needs its own version.
|
||||||
|
|
||||||
|
(use-modules (ice-9 syncase))
|
||||||
|
|
||||||
|
(define (PrintDiagnostics)
|
||||||
|
(display " Total memory available= ???????? bytes")
|
||||||
|
(display " Free memory= ???????? bytes")
|
||||||
|
(newline))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(define (run-benchmark str thu)
|
||||||
|
(display str)
|
||||||
|
(thu))
|
||||||
|
; Should we implement a Java class as procedures or hygienic macros?
|
||||||
|
; Take your pick.
|
||||||
|
|
||||||
|
(define-syntax let-class
|
||||||
|
(syntax-rules
|
||||||
|
()
|
||||||
|
|
||||||
|
;; Put this rule first to implement a class using procedures.
|
||||||
|
((let-class (((method . args) . method-body) ...) . body)
|
||||||
|
(let () (define (method . args) . method-body) ... . body))
|
||||||
|
|
||||||
|
|
||||||
|
;; Put this rule first to implement a class using hygienic macros.
|
||||||
|
((let-class (((method . args) . method-body) ...) . body)
|
||||||
|
(letrec-syntax ((method (syntax-rules () ((method . args) (begin . method-body))))
|
||||||
|
...)
|
||||||
|
. body))
|
||||||
|
|
||||||
|
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
(define (gcbench kStretchTreeDepth)
|
||||||
|
|
||||||
|
; Nodes used by a tree of a given size
|
||||||
|
(define (TreeSize i)
|
||||||
|
(- (expt 2 (+ i 1)) 1))
|
||||||
|
|
||||||
|
; Number of iterations to use for a given tree depth
|
||||||
|
(define (NumIters i)
|
||||||
|
(quotient (* 2 (TreeSize kStretchTreeDepth))
|
||||||
|
(TreeSize i)))
|
||||||
|
|
||||||
|
; Parameters are determined by kStretchTreeDepth.
|
||||||
|
; In Boehm's version the parameters were fixed as follows:
|
||||||
|
; public static final int kStretchTreeDepth = 18; // about 16Mb
|
||||||
|
; public static final int kLongLivedTreeDepth = 16; // about 4Mb
|
||||||
|
; public static final int kArraySize = 500000; // about 4Mb
|
||||||
|
; public static final int kMinTreeDepth = 4;
|
||||||
|
; public static final int kMaxTreeDepth = 16;
|
||||||
|
; In Larceny the storage numbers above would be 12 Mby, 3 Mby, 6 Mby.
|
||||||
|
|
||||||
|
(let* ((kLongLivedTreeDepth (- kStretchTreeDepth 2))
|
||||||
|
(kArraySize (* 4 (TreeSize kLongLivedTreeDepth)))
|
||||||
|
(kMinTreeDepth 4)
|
||||||
|
(kMaxTreeDepth kLongLivedTreeDepth))
|
||||||
|
|
||||||
|
; Elements 3 and 4 of the allocated vectors are useless.
|
||||||
|
|
||||||
|
(let-class (((make-node l r)
|
||||||
|
(let ((v (make-empty-node)))
|
||||||
|
(vector-set! v 0 l)
|
||||||
|
(vector-set! v 1 r)
|
||||||
|
v))
|
||||||
|
((make-empty-node) (make-vector 4 0))
|
||||||
|
((node.left node) (vector-ref node 0))
|
||||||
|
((node.right node) (vector-ref node 1))
|
||||||
|
((node.left-set! node x) (vector-set! node 0 x))
|
||||||
|
((node.right-set! node x) (vector-set! node 1 x)))
|
||||||
|
|
||||||
|
; Build tree top down, assigning to older objects.
|
||||||
|
(define (Populate iDepth thisNode)
|
||||||
|
(if (<= iDepth 0)
|
||||||
|
#f
|
||||||
|
(let ((iDepth (- iDepth 1)))
|
||||||
|
(node.left-set! thisNode (make-empty-node))
|
||||||
|
(node.right-set! thisNode (make-empty-node))
|
||||||
|
(Populate iDepth (node.left thisNode))
|
||||||
|
(Populate iDepth (node.right thisNode)))))
|
||||||
|
|
||||||
|
; Build tree bottom-up
|
||||||
|
(define (MakeTree iDepth)
|
||||||
|
(if (<= iDepth 0)
|
||||||
|
(make-empty-node)
|
||||||
|
(make-node (MakeTree (- iDepth 1))
|
||||||
|
(MakeTree (- iDepth 1)))))
|
||||||
|
|
||||||
|
(define (TimeConstruction depth)
|
||||||
|
(let ((iNumIters (NumIters depth)))
|
||||||
|
(display (string-append "Creating "
|
||||||
|
(number->string iNumIters)
|
||||||
|
" trees of depth "
|
||||||
|
(number->string depth)))
|
||||||
|
(newline)
|
||||||
|
(run-benchmark "GCBench: Top down construction"
|
||||||
|
(lambda ()
|
||||||
|
(do ((i 0 (+ i 1)))
|
||||||
|
((>= i iNumIters))
|
||||||
|
(Populate depth (make-empty-node)))))
|
||||||
|
(run-benchmark "GCBench: Bottom up construction"
|
||||||
|
(lambda ()
|
||||||
|
(do ((i 0 (+ i 1)))
|
||||||
|
((>= i iNumIters))
|
||||||
|
(MakeTree depth))))))
|
||||||
|
|
||||||
|
(define (main)
|
||||||
|
(display "Garbage Collector Test")
|
||||||
|
(newline)
|
||||||
|
(display (string-append
|
||||||
|
" Stretching memory with a binary tree of depth "
|
||||||
|
(number->string kStretchTreeDepth)))
|
||||||
|
(newline)
|
||||||
|
(run-benchmark "GCBench: Main"
|
||||||
|
(lambda ()
|
||||||
|
; Stretch the memory space quickly
|
||||||
|
(MakeTree kStretchTreeDepth)
|
||||||
|
|
||||||
|
; Create a long lived object
|
||||||
|
(display (string-append
|
||||||
|
" Creating a long-lived binary tree of depth "
|
||||||
|
(number->string kLongLivedTreeDepth)))
|
||||||
|
(newline)
|
||||||
|
(let ((longLivedTree (make-empty-node)))
|
||||||
|
(Populate kLongLivedTreeDepth longLivedTree)
|
||||||
|
|
||||||
|
; Create long-lived array, filling half of it
|
||||||
|
(display (string-append
|
||||||
|
" Creating a long-lived array of "
|
||||||
|
(number->string kArraySize)
|
||||||
|
" inexact reals"))
|
||||||
|
(newline)
|
||||||
|
(let ((array (make-vector kArraySize 0.0)))
|
||||||
|
(do ((i 0 (+ i 1)))
|
||||||
|
((>= i (quotient kArraySize 2)))
|
||||||
|
(vector-set! array i (/ 1.0 (exact->inexact i))))
|
||||||
|
(PrintDiagnostics)
|
||||||
|
|
||||||
|
(do ((d kMinTreeDepth (+ d 2)))
|
||||||
|
((> d kMaxTreeDepth))
|
||||||
|
(TimeConstruction d))
|
||||||
|
|
||||||
|
(if (or (eq? longLivedTree '())
|
||||||
|
(let ((n (min 1000
|
||||||
|
(- (quotient (vector-length array)
|
||||||
|
2)
|
||||||
|
1))))
|
||||||
|
(not (= (vector-ref array n)
|
||||||
|
(/ 1.0 (exact->inexact
|
||||||
|
n))))))
|
||||||
|
(begin (display "Failed") (newline)))
|
||||||
|
; fake reference to LongLivedTree
|
||||||
|
; and array
|
||||||
|
; to keep them from being optimized away
|
||||||
|
))))
|
||||||
|
(PrintDiagnostics))
|
||||||
|
|
||||||
|
(main))))
|
||||||
|
|
||||||
|
(define (gc-benchmark . rest)
|
||||||
|
(let ((k (if (null? rest) 18 (car rest))))
|
||||||
|
(display "The garbage collector should touch about ")
|
||||||
|
(display (expt 2 (- k 13)))
|
||||||
|
(display " megabytes of heap storage.")
|
||||||
|
(newline)
|
||||||
|
(display "The use of more or less memory will skew the results.")
|
||||||
|
(newline)
|
||||||
|
(run-benchmark (string-append "GCBench" (number->string k))
|
||||||
|
(lambda () (gcbench k)))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(gc-benchmark )
|
||||||
|
(display (gc-stats))
|
9
gc-benchmarks/guile-test.scm
Normal file
9
gc-benchmarks/guile-test.scm
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
(set! %load-path (cons (string-append (getenv "HOME") "/src/guile")
|
||||||
|
%load-path))
|
||||||
|
|
||||||
|
(load "../test-suite/guile-test")
|
||||||
|
|
||||||
|
(main `("guile-test"
|
||||||
|
"--test-suite" ,(string-append (getenv "HOME")
|
||||||
|
"/src/guile/test-suite/tests")
|
||||||
|
"--log-file" ",,test-suite.log"))
|
4
gc-benchmarks/loop.scm
Normal file
4
gc-benchmarks/loop.scm
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
(let loop ((i 10000000))
|
||||||
|
(and (> i 0)
|
||||||
|
(loop (1- i))))
|
||||||
|
|
25
gc-benchmarks/string.scm
Normal file
25
gc-benchmarks/string.scm
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
;;; From from http://www.ccs.neu.edu/home/will/Twobit/KVW/string.txt .
|
||||||
|
; string test
|
||||||
|
; (try 100000)
|
||||||
|
|
||||||
|
(define s "abcdef")
|
||||||
|
|
||||||
|
(define (grow)
|
||||||
|
(set! s (string-append "123" s "456" s "789"))
|
||||||
|
(set! s (string-append
|
||||||
|
(substring s (quotient (string-length s) 2) (string-length s))
|
||||||
|
(substring s 0 (+ 1 (quotient (string-length s) 2)))))
|
||||||
|
s)
|
||||||
|
|
||||||
|
(define (trial n)
|
||||||
|
(do ((i 0 (+ i 1)))
|
||||||
|
((> (string-length s) n) (string-length s))
|
||||||
|
(grow)))
|
||||||
|
|
||||||
|
(define (try n)
|
||||||
|
(do ((i 0 (+ i 1)))
|
||||||
|
((>= i 10) (string-length s))
|
||||||
|
(set! s "abcdef")
|
||||||
|
(trial n)))
|
||||||
|
|
||||||
|
(try 50000000)
|
Loading…
Add table
Add a link
Reference in a new issue