add compute-size-increments

Also adds `get-initial-thread`, since threa values are useful with
`compute-size[-increments]`.

Changes the compiler to inline `weak-pair?` and `ephemeron-pair?`,
since that provides better performance for `compute-size-increments`.

original commit: 57d0cc13f8e932972cba3837b4f54e9c86786091
This commit is contained in:
Matthew Flatt 2019-01-03 08:33:20 -07:00
parent 03a33fb4fc
commit aaaa5fefa1
15 changed files with 614 additions and 53 deletions

5
LOG
View File

@ -1027,3 +1027,8 @@
cpnanopass.ss, x86_64.ss, x86.ss, foreign2.c, foreign.ms
- added initialization of seginfo sorted and trigger_ephemerons fields.
segment.c
- added `compute-size-increments` and `get-initial-thread`, and
adjust compiler to inline `weak-pair?` and `ephemeron-pair?`
inspect.ss, bitset.ss, cmacros.ss, prims.ss, primdata.ss, Mf-base,
prim.c, types.h, misc.ms, thread.ms, debug.stex, threads.stex,
release_notes.stex

View File

@ -139,6 +139,7 @@ static void create_c_entry_vector() {
install_c_entry(CENTRY_Sreturn, proc2ptr(S_return));
install_c_entry(CENTRY_Scall_one_result, proc2ptr(S_call_one_result));
install_c_entry(CENTRY_Scall_any_results, proc2ptr(S_call_any_results));
install_c_entry(CENTRY_segment_info, proc2ptr(S_segment_info));
for (i = 0; i < c_entry_vector_size; i++) {
#ifndef PTHREADS

View File

@ -115,6 +115,8 @@ typedef int IFASLCODE; /* fasl type codes */
#define ptr_align(size) (((size)+byte_alignment-1) & ~(byte_alignment-1))
/* The inlined implementation of primitives like `weak-pair?`
rely on the first two fields of `seginfo`: */
typedef struct _seginfo {
unsigned char space; /* space the segment is in */
unsigned char generation; /* generation the segment is in */

View File

@ -1452,6 +1452,14 @@ of all objects in the heap is sufficient,
\index{\scheme{object-counts}}\scheme{object-counts} is potentially much
more efficient.
The procedure \scheme{compute-size-increments} is similar to
\scheme{compute-size} mapped over a list of elements, but it reports a
size for each later element without including the size of objects
reachable from ealier elements, and it treats weak and ephemeron pairs
differently. For a large number of reachable objects,
\scheme{compute-size-increments} uses much less memory than
\scheme{compute-size}.
These procedures treat immediate objects such as fixnums, booleans, and
characters as zero-count, zero-byte leaves.
@ -1495,6 +1503,11 @@ If \var{generation} is not supplied, it defaults to the value of
\var{object} and anything reachable from \var{object} in any generation
less than or equal to \var{generation}.
Immediate values such as fixnums, booleans, and characters have zero size.
Size computation for a thread is limited when the thread is still active,
since its full continuation cannot be inspected in that case, but the
full continuation is inspected if the thread is inactive (such as during
a garbage collection rendezvous when a different thread is selected by
the rendezvous).
The following examples are valid for machines with 32-bit pointers.
@ -1569,3 +1582,52 @@ The following examples are valid for machines with 32-bit pointers.
0) ;=> ((pair 1 . 8)
(#<record type frob> 1 . 8))
\endschemedisplay
\entryheader
\formdef{compute-size-increments}{\categoryprocedure}{(compute-size-increments \var{list})}
\formdef{compute-size-increments}{\categoryprocedure}{(compute-size-increments \var{list} \var{generation})}
\returns a list as described below
\listlibraries
\endentryheader
\var{list} must be a list, but each element can be any object.
\var{generation} must be a fixnum between 0 and the value of
\scheme{collect-maximum-generation}, inclusive, or the symbol
\scheme{static}.
If \var{generation} is not supplied, it defaults to the value of
\scheme{collect-maximum-generation}.
\scheme{compute-size-increments} is like mapping \scheme{compute-size}
over \var{list}, except that any object reachable from an earlier
element of \var{list} is not treated as reachable by a later element
of \var{list}. In addition, each immediate element of \var{list} is
not treated as reachable by earlier elements of \var{list}---although
other values reachable from later elements of \var{list} may be
considered reachable from earlier elements.
Unlike \var{compute-size}, \scheme{compute-size-increments} does not
consider the \scheme{car} of a weak pair reachable from the weak pair.
It also does not consider the \scheme{car} or \scheme{cdr} of an
ephemeron pair to be reachable from the ephemeron pair, unless the
\scheme{car} is already determined to be reachable (perhaps from an
earlier element of \var{list}); if the \scheme{car} of an ephemeron
pair is discovered to be reachable later (perhaps from a later element
of \var{list}), then the \scheme{cdr} of the ephemeron pair is
considered to be reachable from the \scheme{car}, which has the effect
of charging the memory of the \scheme{cdr} to the same element of
\var{list} as the memory of the \scheme{car}.
The following examples are valid for machines with 32-bit pointers.
\schemedisplay
(compute-size-increments (list 0)) ;=> (0)
(compute-size-increments (list (cons 0 0))) ;=> (8)
(compute-size-increments (list (cons 0 0) (cons 0 0))) ;=> (8 8)
(compute-size-increments (let ([p (cons 0 0)])
(list p p))) ;=> (8 0)
(compute-size-increments (let ([p (cons 0 0)])
(list (cons 1 p) (cons 1 p)))) ;=> (16 8)
(compute-size-increments (let* ([p (cons 0 0)]
[e (ephemeron-cons p (cons 0 0))])
(list e p))) ;=> (8 16)
\endschemedisplay

View File

@ -90,13 +90,22 @@ synchronized across processors.
a thread object.
Nothing can be done with the thread object returned by
\scheme{fork-thread}, other than to print it.
\scheme{fork-thread}, other than to print it or use it with inspection
functions such as \scheme{compute-size}.
Threads created by foreign code using some means other than
\scheme{fork-thread} must call \scheme{Sactivate_thread}
(Section~\ref{SECTFOREIGNCLIB}) before touching any Scheme data
or calling any Scheme procedures.
%----------------------------------------------------------------------------
\entryheader
\formdef{get-initial-thread}{\categoryprocedure}{(get-initial-thread)}
\returns a thread object for the initial thread
\listlibraries
\endentryheader
%----------------------------------------------------------------------------
\entryheader
\formdef{thread?}{\categoryprocedure}{(thread? \var{obj})}
@ -117,7 +126,6 @@ relationship to the process id returned by
\index{\scheme{get-process-id}}\scheme{get-process-id}, which is the same
in all threads.
\section{Mutexes}
%----------------------------------------------------------------------------

View File

@ -1049,6 +1049,108 @@
(fixnum? (let ([k (call/1cc (lambda (k) k))]) (collect) (compute-size k)))
)
(mat compute-size-increments
(error? (compute-size-increments 'not-a-list))
(error? (compute-size-increments 0))
(error? (compute-size-increments (list 0) -1))
(error? (compute-size-increments (list 0) "static"))
(error? (compute-size-increments (list 0) '()))
(begin
(define pair-size (compute-size (cons 1 2)))
#t)
(equal? (list pair-size pair-size)
(compute-size-increments (list (cons 1 2) (cons 3 4))))
(equal? (list (* 3 pair-size) pair-size)
(let ([l (list 1 2)])
(compute-size-increments (list (cons 3 l) (cons 4 l)))))
(equal? (list pair-size)
(compute-size-increments (list (weak-cons (make-bytevector 100) #f))))
(let* ([x (make-bytevector 100)]
[ls (list (lambda () x) x)])
(equal? (compute-size-increments ls)
(reverse (compute-size-increments (reverse ls)))))
;; Ephemeron(s) found before key:
(equal? (list pair-size (* 2 pair-size))
(compute-size-increments (let* ([p (cons 0 0)]
[e (ephemeron-cons p (cons 0 0))])
(list e p))))
(equal? (list pair-size (* 3 pair-size))
(let* ([v (cons 1 2)]
[e (ephemeron-cons v (cons 3 4))])
(compute-size-increments (list e (cons v #f)))))
(equal? (list (* 4 pair-size) (* 4 pair-size))
(let* ([v (cons 1 2)]
[e* (list (ephemeron-cons v (cons 3 4))
(ephemeron-cons v (cons 5 6)))])
(compute-size-increments (list e* (cons v #f)))))
;; Key found before ephemeron(s):
(equal? (list (* 2 pair-size) (* 2 pair-size))
(let* ([v (cons 1 2)]
[e (ephemeron-cons v (cons 3 4))])
(compute-size-increments (list (cons v #f) e))))
(equal? (list (* 2 pair-size) (* 6 pair-size))
(let* ([v (cons 1 2)]
[e* (list (ephemeron-cons v (cons 3 4))
(ephemeron-cons v (cons 5 6)))])
(compute-size-increments (list (cons v #f) e*))))
;; This call will encounter many kinds of objects, just to make
;; sure it doesn't fail:
(list? (compute-size-increments (list (call/cc values)) 'static))
;; Check that a deactivated thread's continuation can be traversed
;; for `compute-size-increments`:
(or (not (threaded?))
(let* ([ready (box #f)]
[saved (box #f)]
[m (make-mutex)]
[N 1000000]
[pause-until (lambda (check)
(let loop ()
(unless (check)
(sleep (make-time 'time-duration 10000 0))
(loop))))]
[th (fork-thread
(lambda ()
(let ([bstr (make-bytevector N)])
(box-cas! ready #f 'go)
;; Block so that thread becomes deactivated
(mutex-acquire m)
(mutex-release m)
;; bstr is retained in the thread's continuation until here
(set-box! saved (bytevector-u8-ref bstr 0))
(pause-until (lambda () (box-cas! ready 'finish 'done)))
;; Block so that thread becomes deactivated, again
(mutex-acquire m)
(mutex-release m))))])
(mutex-acquire m)
;; Wait for thread to start
(pause-until (lambda () (eq? 'go (unbox ready))))
;; Wait for thread to become inactive, blocked on the mutex
(pause-until (lambda () (= 1 (#%$top-level-value '$active-threads))))
;; Get thread's size, which should include bstr
(let ([pre-sizes (compute-size-increments (list th))])
(mutex-release m)
;; Wait for bytevector to be discarded in the thread
(pause-until (lambda () (unbox saved)))
(mutex-acquire m)
(set-box! ready 'finish)
;; Wait for thread to become inactive again
(pause-until (lambda () (= 1 (#%$top-level-value '$active-threads))))
;; Get thread's size, which should'nt include bstr
(let ([post-sizes (compute-size-increments (list th))])
(mutex-release m)
;; Wait for thread to exit
(let ()
(define $threads (foreign-procedure "(cs)threads" () scheme-object))
(pause-until (lambda () (= 1 (length ($threads))))))
;; Make sure `compute-size-increments` doesn't crash on a
;; terminated thread:
(compute-size-increments (list th))
;; Main result: detected size of `bstr` in the thread
;; while it was part of the continuation
(and (> (car pre-sizes) N)
(< (car post-sizes) N))))))
)
(mat compute-composition
(error? (compute-composition 0 -1))
(error? (compute-composition 0 "static"))

View File

@ -89,6 +89,7 @@
(not (thread-condition? m))
(not (mutex? 'mutex))
(not (thread-condition? 'condition))))
(thread? (get-initial-thread))
(begin
(define $threads (foreign-procedure "(cs)threads" () scheme-object))
(define $fib (lambda (x) (if (< x 2) 1 (+ ($fib (- x 1)) ($fib (- x 2))))))
@ -98,6 +99,8 @@
(lambda () (sleep t))))
(define $thread-check
(lambda ()
(unless (memq (get-initial-thread) ($threads))
(errorf #f "initial thread is missing from list"))
(let loop ([n 100] [nt (length ($threads))])
(cond
[(<= nt $nthreads)

View File

@ -58,6 +58,15 @@ Online versions of both books can be found at
%-----------------------------------------------------------------------------
\section{Functionality Changes}\label{section:functionality}
\subsection{Counting reachable objects from multiple sources (9.5.1)}
The new procedure \scheme{compute-size-increments} is like
\scheme{compute-size}, but it reports sizes for only newly reached
objects for later elements of a given list. It also treats weak and
ephemeron pairs in a way that assigns sizes to references that cause a
weakly held value to be retained, as opposed to assigning the size to
the pair.
\subsection{Procedure source location without inspector information (9.5.1)}
When \scheme{generate-inspector-information} is set to \scheme{#f} and

View File

@ -149,7 +149,7 @@ macroobj =\
allsrc =\
${basesrc} ${compilersrc} cmacros.ss ${archincludes} setup.ss debug.ss priminfo.ss primdata.ss layout.ss\
base-lang.ss expand-lang.ss primref.ss types.ss io-types.ss fasl-helpers.ss hashtable-types.ss\
np-languages.ss
np-languages.ss bitset.ss
# doit uses a different Scheme process to compile each target
doit: ${PetiteBoot} ${SchemeBoot} ${Cheader} ${Cequates}
@ -497,6 +497,7 @@ setup.so: debug.ss
${patchobj}: ${macroobj} nanopass.so base-lang.ss expand-lang.ss primref.ss types.ss io-types.ss fasl-helpers.ss hashtable-types.ss env.ss
cpnanopass.$m cpnanopass.patch: nanopass.so np-languages.ss ${archincludes}
5_4.$m: ../unicode/unicode-char-cases.ss ../unicode/unicode-charinfo.ss
inspect.$m: bitset.ss
${Cheader}: mkheader.so ${macroobj} nanopass.so base-lang.ss expand-lang.ss primref.ss types.ss io-types.ss fasl-helpers.ss hashtable-types.ss
(if [ -r ${Cheader} ]; then mv -f ${Cheader} ${Cheader}.bak; fi)

73
s/bitset.ss Normal file
View File

@ -0,0 +1,73 @@
;; The eq-bitset implementation assumes that the addresses of an
;; object won't change, so only use an eq-bitset while interrupts are
;; disabled from start to end of the eq-bitset use. The eq-bitset
;; implementation also assumes that `$fxaddress` is a perfect hash for
;; a non-immediate object.
(define (make-eq-bitset)
(make-vector (fxsll 1 (constant eq-bitset-l1-bits)) #f))
(constant-case eq-bitset-l2-bits
[(0)
(define (get-l1 bm n)
(let* ([l1 (fxsrl n (constant eq-bitset-l1-shift))]
[bm1 (vector-ref bm l1)])
(or bm1
(let ([bm1 (make-fxvector (fxsll 1 (constant eq-bitset-l4-bits)) 0)])
(vector-set! bm l1 bm1)
bm1))))
(define (get-l2 bm1 n) bm1)
(define (get-l3 bm1 n) bm1)]
[else
(define (get-l1 bm n)
(let* ([l1 (fxsrl n (constant eq-bitset-l1-shift))]
[bm1 (vector-ref bm l1)])
(or bm1
(let ([bm1 (make-vector (fxsll 1 (constant eq-bitset-l2-bits)) #f)])
(vector-set! bm l1 bm1)
bm1))))
(define (get-l2 bm1 n)
(let* ([l2 (fxand (fxsrl n (constant eq-bitset-l2-shift))
(constant eq-bitset-l2-mask))]
[bm2 (vector-ref bm1 l2)])
(or bm2
(let ([bm2 (make-vector (fxsll 1 (constant eq-bitset-l3-bits)) #f)])
(vector-set! bm1 l2 bm2)
bm2))))
(define (get-l3 bm2 n)
(let* ([l3 (fxand (fxsrl n (constant eq-bitset-l3-shift))
(constant eq-bitset-l3-mask))]
[bm3 (vector-ref bm2 l3)])
(or bm3
(let ([bm3 (make-fxvector (fxsll 1 (constant eq-bitset-l4-bits)) 0)])
(vector-set! bm2 l3 bm3)
bm3))))])
(define (get-l4-index n)
(fxand (fxsrl n (constant eq-bitset-l4-shift)) (constant eq-bitset-l4-mask)))
(define (get-lo-index n)
(fxand (fxsrl n (constant eq-bitset-discard-bits))
(constant eq-bitset-lo-mask)))
(define (eq-bitset-member? bm p)
(let* ([n ($fxaddress p)]
[a (get-l3 (get-l2 (get-l1 bm n) n) n)]
[i (get-l4-index n)])
(fxbit-set? (fxvector-ref a i) (get-lo-index n))))
(define (eq-bitset-add! bm p)
(let* ([n ($fxaddress p)]
[a (get-l3 (get-l2 (get-l1 bm n) n) n)]
[i (get-l4-index n)])
(fxvector-set! a i (fxior (fxvector-ref a i)
(fxsll 1 (get-lo-index n))))))
(define (eq-bitset-remove! bm p)
(let* ([n ($fxaddress p)]
[a (get-l3 (get-l2 (get-l1 bm n) n) n)]
[i (get-l4-index n)])
(fxvector-set! a i (fxand (fxvector-ref a i)
(fxnot (fxsll 1 (get-lo-index n)))))))

View File

@ -1750,6 +1750,50 @@
(syntax-rules ()
((_ x) (let ((t x)) (and (pair? t) (symbol? (car t)))))))
;;; bitset constants
;; For a bitset ranging over all fixnum values, use an array of ...
;; array of fixnums, where the lo bits of a key fixnum are used to
;; index a bit within one bitset fixnum.
(define-constant eq-bitset-lo-bits (fx- (integer-length (constant fixnum-bits)) 1))
;; Using `$fxaddress` discards typemod bits, but we may be able to
;; discard additional bits due to allocation alignment:
(define-constant eq-bitset-discard-bits (fx- (log2 (constant byte-alignment))
(log2 (constant typemod))))
(constant-case ptr-bits
[(64)
;; Break fixnum into 5 levels: [l1:14] [l2:14] [l3:14] [l4:14-discard] [lo:5]
(define-constant eq-bitset-l1-bits 14)
(define-constant eq-bitset-l2-bits 14)
(define-constant eq-bitset-l3-bits 14)]
[(32)
;; Break fixnum into 3 levels: [l1:13] [l4:13-discard] [lo:4]
(define-constant eq-bitset-l1-bits 13)
(define-constant eq-bitset-l2-bits 0)
(define-constant eq-bitset-l3-bits 0)])
(define-constant eq-bitset-l4-bits (fx- (constant fixnum-bits)
(constant eq-bitset-l1-bits)
(constant eq-bitset-l2-bits)
(constant eq-bitset-l3-bits)
(constant eq-bitset-lo-bits)
(constant eq-bitset-discard-bits)))
(define-constant eq-bitset-l1-shift (fx- (constant fixnum-bits) (constant eq-bitset-l1-bits)))
(define-constant eq-bitset-l2-shift (fx- (constant fixnum-bits) (constant eq-bitset-l1-bits)
(constant eq-bitset-l2-bits)))
(define-constant eq-bitset-l3-shift (fx- (constant fixnum-bits) (constant eq-bitset-l1-bits)
(constant eq-bitset-l2-bits) (constant eq-bitset-l3-bits)))
(define-constant eq-bitset-l4-shift (fx+ (constant eq-bitset-lo-bits)
(constant eq-bitset-discard-bits)))
(define-constant eq-bitset-l2-mask (fx- (fxsll 1 (constant eq-bitset-l2-bits)) 1))
(define-constant eq-bitset-l3-mask (fx- (fxsll 1 (constant eq-bitset-l3-bits)) 1))
(define-constant eq-bitset-l4-mask (fx- (fxsll 1 (constant eq-bitset-l4-bits)) 1))
(define-constant eq-bitset-lo-mask (fx- (fxsll 1 (constant eq-bitset-lo-bits)) 1))
;;; heap/stack mangement constants
(define-constant collect-interrupt-index 1)
@ -2645,5 +2689,6 @@
Sreturn
Scall-one-result
Scall-any-results
segment-info
))
)

View File

@ -5019,6 +5019,102 @@
(inline-accessor port-name port-name-disp)
(inline-accessor $thread-tc thread-tc-disp)
)
(let ()
(define (build-maybe-seginfo e)
(let ([ptr (make-assigned-tmp 'ptr)]
[seginfo (make-assigned-tmp 'seginfo)])
(define (build-level-3 seginfo k)
(constant-case segment-table-levels
[(3)
(let ([s3 (make-assigned-tmp 's3)])
`(let ([,s3 ,(%mref ,seginfo
,(%inline sll ,(%inline srl ,ptr (immediate ,(+ (constant segment-t1-bits)
(constant segment-t2-bits))))
(immediate ,(constant log2-ptr-bytes)))
,0)])
(if ,(%inline eq? ,s3 (immediate 0))
(immediate 0)
,(k s3))))]
[else (k seginfo)]))
(define (build-level-2 s3 k)
(constant-case segment-table-levels
[(2 3)
(let ([s2 (make-assigned-tmp 's2)])
`(let ([,s2 ,(%mref ,s3 ,(%inline logand
,(%inline srl ,ptr (immediate ,(fx- (constant segment-t1-bits)
(constant log2-ptr-bytes))))
(immediate ,(fxsll (fx- (fxsll 1 (constant segment-t2-bits)) 1)
(constant log2-ptr-bytes))))
0)])
(if ,(%inline eq? ,s2 (immediate 0))
(immediate 0)
,(k s2))))]
[else (k s3)]))
`(let ([,ptr ,(%inline srl ,(%inline + ,e (immediate ,(fx- (constant typemod) 1)))
(immediate ,(constant segment-offset-bits)))])
(let ([,seginfo (literal ,(make-info-literal #f 'entry (lookup-c-entry segment-info) 0))])
,(build-level-3 seginfo
(lambda (s3)
(build-level-2 s3
(lambda (s2)
(%mref ,s2 ,(%inline sll ,(%inline logand ,ptr
(immediate ,(fx- (fxsll 1 (constant segment-t1-bits)) 1)))
(immediate ,(constant log2-ptr-bytes)))
0)))))))))
(define (build-space-test e space)
`(if ,(%type-check mask-fixnum type-fixnum ,e)
,(%constant sfalse)
(if ,(%type-check mask-immediate type-immediate ,e)
,(%constant sfalse)
,(let ([s-e (build-maybe-seginfo e)]
[si (make-assigned-tmp 'si)])
`(let ([,si ,s-e])
(if ,(%inline eq? ,si (immediate 0))
,(%constant sfalse)
,(let ([s `(inline ,(make-info-load 'unsigned-8 #f) ,%load ,si ,%zero (immediate 0))])
(%inline eq? (immediate ,space)
,(%inline logand ,s (immediate ,(fxnot (constant space-locked))))))))))))
(define-inline 2 $maybe-seginfo
[(e)
(bind #t (e)
`(if ,(%type-check mask-fixnum type-fixnum ,e)
,(%constant sfalse)
(if ,(%type-check mask-immediate type-immediate ,e)
,(%constant sfalse)
,(let ([s-e (build-maybe-seginfo e)]
[si (make-assigned-tmp 'si)])
`(let ([,si ,s-e])
(if ,(%inline eq? ,si (immediate 0))
,(%constant sfalse)
,si))))))])
;; Generation is first unsigned char in `seginfo` as defined in "types.h"
(define-inline 2 $seginfo-generation
[(e)
(bind #f (e) (build-object-ref #f 'unsigned-8 e %zero 1))])
;; Space is second unsigned char in `seginfo` as defined in "types.h"
(define-inline 2 $seginfo-space
[(e)
(bind #f (e)
(%inline logand ,(build-object-ref #f 'unsigned-8 e %zero 0)
(immediate ,(fxnot (fix (constant space-locked))))))])
(define-inline 2 $generation
[(e)
(bind #t (e)
`(if ,(%type-check mask-fixnum type-fixnum ,e)
,(%constant sfalse)
,(let ([s-e (build-maybe-seginfo e)]
[si (make-assigned-tmp 'si)])
`(let ([,si ,s-e])
(if ,(%inline eq? ,si (immediate 0))
,(%constant sfalse)
,(build-object-ref #f 'unsigned-8 si %zero 1))))))])
(define-inline 2 weak-pair?
[(e) (bind #t (e) (build-space-test e (constant space-weakpair)))])
(define-inline 2 ephemeron-pair?
[(e) (bind #t (e) (build-space-test e (constant space-ephemeron)))]))
(define-inline 2 unbox
[(e)
(bind #t (e)

View File

@ -2389,7 +2389,6 @@
(let ()
(define rtd-size (csv7:record-field-accessor #!base-rtd 'size))
(define rtd-flds (csv7:record-field-accessor #!base-rtd 'flds))
(define $generation (foreign-procedure "(cs)generation" (ptr) ptr))
(define $get-code-obj (foreign-procedure "(cs)get_code_obj" (int ptr iptr iptr) ptr))
(define $code-reloc-size
(lambda (x)
@ -2427,41 +2426,134 @@
(define align
(lambda (n)
(fxlogand (fx+ n (fx- (constant byte-alignment) 1)) (fx- (constant byte-alignment)))))
(include "bitset.ss")
(set-who! $compute-size
(rec $compute-size
(define (thread->stack-objects thread)
(with-tc-mutex
(let ([tc ($thread-tc thread)])
(cond
[(eqv? tc 0)
;; Thread terminated
'()]
[(zero? ($object-ref 'integer-32 tc (constant tc-active-disp)))
;; Inactive, so we can traverse it while holding the tc mutex
(let ([stack ($object-ref 'scheme-object tc (constant tc-scheme-stack-disp))])
(let loop ([frame ($object-ref 'scheme-object tc (constant tc-sfp-disp))] [x* '()])
(cond
[(fx= frame stack)
x*]
[else
(let* ([ret ($object-ref 'scheme-object frame 0)]
[size ($object-ref 'scheme-object ret (constant return-address-frame-size-disp))]
[livemask ($object-ref 'scheme-object ret (constant return-address-livemask-disp))]
[next-frame (fx- frame size)])
(let frame-loop ([p (fx+ next-frame 1)] [livemask livemask] [x* x*])
(if (eqv? livemask 0)
(loop next-frame x*)
(frame-loop (fx+ p 1)
(bitwise-arithmetic-shift-right livemask 1)
(if (bitwise-bit-set? livemask 0)
(cons ($object-ref 'scheme-object p 0) x*)
x*)))))])))]
[else
;; Can't inspect active thread
'()]))))
(define (thread->objects thread)
;; Get immediate content while holding the tc mutex to be sure
;; that the thread doesn't terminate while getting its content
(with-tc-mutex
(let ([tc ($thread-tc thread)])
(cond
[(eqv? tc 0)
;; Thread terminated
'()]
[else
(map (lambda (disp) ($object-ref 'scheme-object tc disp))
tc-ptr-offsets)]))))
;; call with interrupts disabled if not `single-inspect-mode?`
(set-who! $compute-size-increments
(rec $compute-size-increments
(case-lambda
[(x maxgen) ($compute-size x maxgen (make-eq-hashtable))]
[(x maxgen size-ht)
(define cookie (cons 'date 'nut)) ; recreate on each call to $compute-size
[(x* maxgen) ($compute-size-increments x* maxgen #f (make-eq-bitset))]
[(x* maxgen single-inspect-mode? size-ht-or-bitset)
(define ephemeron-triggers #f)
(define ephemeron-triggers-bitset #f)
(define ephemeron-non-keys (and (not single-inspect-mode?) (make-eq-hashtable)))
(define cookie (and single-inspect-mode?
(cons 'date 'nut))) ; recreate on each call to $compute-size-increments
(define compute-size
(lambda (x)
(if (or ($immediate? x)
(let ([g ($generation x)])
(or (not g) (fx> g maxgen))))
0
(let ([a (eq-hashtable-cell size-ht x #f)])
(let ([si ($maybe-seginfo x)])
(cond
[(or (not si)
(fx> ($seginfo-generation si) maxgen))
0]
[single-inspect-mode?
(let ([a (eq-hashtable-cell size-ht-or-bitset x #f)])
(cond
[(cdr a) =>
(lambda (p)
; if we find our cookie, return 0 to avoid counting shared structure twice.
; otherwise, (car p) must be a cookie from an earlier call to $compute-size,
; so return the recorded size
(if (eq? (car p) cookie)
0
(begin
(set-car! p cookie)
(cdr p))))]
[else
(let ([p (cons cookie 0)])
(set-cdr! a p)
(let ([size (really-compute-size x)])
(set-cdr! p size)
size))])))))
[(cdr a) =>
(lambda (p)
; if we find our cookie, return 0 to avoid counting shared structure twice.
; otherwise, (car p) must be a cookie from an earlier call to $compute-size,
; so return the recorded size
(if (eq? (car p) cookie)
0
(begin
(set-car! p cookie)
(cdr p))))]
[else
(let ([p (cons cookie 0)])
(set-cdr! a p)
(let ([size (really-compute-size x si)])
(set-cdr! p size)
size))]))]
[else
(cond
[(eq-bitset-member? size-ht-or-bitset x) 0]
[else
(eq-bitset-add! size-ht-or-bitset x)
(let ([size (really-compute-size x si)])
(let ([ds (and ephemeron-triggers-bitset
(eq-bitset-member? ephemeron-triggers-bitset x)
(eq-hashtable-ref ephemeron-triggers x #f))])
(cond
[ds
(eq-hashtable-delete! ephemeron-triggers x)
(fold-left (lambda (size d) (fx+ size (compute-size d)))
size
ds)]
[else size])))])]))))
(define really-compute-size
(lambda (x)
(lambda (x si)
(cond
[(pair? x) (fx+ (constant size-pair) (compute-size (car x)) (compute-size (cdr x)))]
[(pair? x)
(let ([space ($seginfo-space si)])
(cond
[(and (eqv? space (constant space-weakpair))
(not single-inspect-mode?))
(fx+ (constant size-pair) (compute-size (cdr x)))]
[(and (eqv? space (constant space-ephemeron))
(not single-inspect-mode?)
(let ([a (car x)])
(not (or ($immediate? a)
(let ([g ($generation a)])
(or (not g) (fx> g maxgen)))
(and (eq-bitset-member? size-ht-or-bitset a)
(not (eq-hashtable-ref ephemeron-non-keys a #f)))))))
(let ([d (cdr x)])
(unless ($immediate? d)
(unless ephemeron-triggers-bitset
(set! ephemeron-triggers-bitset (make-eq-bitset))
(set! ephemeron-triggers (make-eq-hashtable)))
(let ([v (car x)])
(eq-bitset-add! ephemeron-triggers-bitset v)
(let ([a (eq-hashtable-cell ephemeron-triggers v '())])
(set-cdr! a (cons d (cdr a)))))))
(constant size-pair)]
[else
(fx+ (constant size-pair) (compute-size (car x)) (compute-size (cdr x)))]))]
[(symbol? x)
(fx+ (constant size-symbol)
(compute-size (#3%$top-level-value x))
@ -2553,12 +2645,13 @@
(compute-size ($port-info x))
(compute-size (port-name x)))]
[(thread? x)
(let ([tc ($object-ref 'scheme-object x (constant thread-tc-disp))])
(fold-left
(lambda (size disp)
(fx+ size (compute-size ($object-ref 'scheme-object tc disp))))
(constant size-thread)
tc-ptr-offsets))]
(fx+ (fold-left (lambda (size x)
(fx+ size (compute-size x)))
(constant size-thread)
(thread->objects x))
(fold-left (lambda (size x) (fx+ size (compute-size x)))
0
(thread->stack-objects x)))]
[($tlc? x)
(fx+ (constant size-tlc)
(compute-size ($tlc-ht x))
@ -2566,9 +2659,35 @@
(compute-size ($tlc-next x)))]
[($rtd-counts? x) (constant size-rtd-counts)]
[else ($oops who "missing case for ~s" x)])))
; ensure size-ht isn't counted in the size of any object
(eq-hashtable-set! size-ht size-ht (cons cookie 0))
(compute-size x)])))
(cond
[single-inspect-mode?
; ensure size-ht isn't counted in the size of any object
(eq-hashtable-set! size-ht-or-bitset size-ht-or-bitset (cons cookie 0))
(map compute-size x*)]
[else
; ensure bitset isn't counted in the size of any object
(eq-bitset-add! size-ht-or-bitset size-ht-or-bitset)
;; Stop at each element of `x` when getting results for other elements,
;; but don't treat later elements as already-reached ephemeron keys:
(for-each (lambda (x)
(eq-bitset-add! size-ht-or-bitset x)
(eq-hashtable-set! ephemeron-non-keys x #t))
x*)
;; Traverse `x*` in order:
(let loop ([x* x*])
(cond
[(null? x*) '()]
[else
(let ([x (car x*)])
(eq-bitset-remove! size-ht-or-bitset x)
(eq-hashtable-delete! ephemeron-non-keys x)
(cons (compute-size x)
(loop (cdr x*))))]))])])))
(set-who! $compute-size
(case-lambda
[(x maxgen) (car ($compute-size-increments (list x) maxgen #t (make-eq-hashtable)))]
[(x maxgen size-ht) (car ($compute-size-increments (list x) maxgen #t size-ht))]))
(set-who! $compute-composition
(lambda (x maxgen)
@ -2708,8 +2827,8 @@
(compute-composition! (port-name x))]
[(thread? x)
(incr! thread (constant size-thread))
(let ([tc ($object-ref 'scheme-object x (constant thread-tc-disp))])
(for-each (lambda (disp) (compute-composition! ($object-ref 'scheme-object tc disp))) tc-ptr-offsets))]
(for-each compute-composition! (thread->objects x))
(for-each compute-composition! (thread->stack-objects x))]
[($tlc? x)
(incr! tlc (constant size-tlc))
(compute-composition! ($tlc-ht x))
@ -2838,10 +2957,12 @@
(if (input-port? x) (construct-proc (port-input-buffer x) (th)) (th))))]
[(thread? x)
(let ([tc ($object-ref 'scheme-object x (constant thread-tc-disp))])
(let f ([disp-list tc-ptr-offsets])
(if (null? disp-list)
next-proc
(construct-proc ($object-ref 'scheme-object tc (car disp-list)) (f (cdr tc-ptr-offsets))))))]
(if (eqv? tc 0)
next-proc
(let f ([disp-list tc-ptr-offsets])
(if (null? disp-list)
next-proc
(construct-proc ($object-ref 'scheme-object tc (car disp-list)) (f (cdr tc-ptr-offsets)))))))]
[($tlc? x) (construct-proc ($tlc-ht x) ($tlc-keyval x) ($tlc-next x) next-proc)]
[else ($oops who "missing case for ~s" x)])])
; check if this node is what we're looking for
@ -2874,6 +2995,16 @@
[(x) ($compute-size x (collect-maximum-generation))]
[(x g) ($compute-size x (filter-generation who g))]))
(set-who! compute-size-increments
(rec compute-size-increments
(case-lambda
[(x*) (compute-size-increments x* (collect-maximum-generation))]
[(x* g)
(unless (list? x*) ($oops who "~s is not a list" x*))
(let ([g (filter-generation who g)])
(with-interrupts-disabled
($compute-size-increments x* g)))])))
(set-who! compute-composition
(case-lambda
[(x) ($compute-composition x (collect-maximum-generation))]

View File

@ -893,6 +893,7 @@
(define-symbol-flags* ([libraries] [flags primitive proc]) ; constant parameters
(directory-separator [sig [() -> (char)]] [flags pure unrestricted true cp02])
(get-initial-thread [sig [() -> (boolean)]] [flags pure unrestricted true])
(get-process-id [sig [() -> (uint)]] [flags pure unrestricted])
(get-thread-id [sig [() -> (uint)]] [flags discard unrestricted])
(machine-type [sig [() -> (symbol)]] [flags pure unrestricted true cp02])
@ -1213,6 +1214,7 @@
(compile-whole-library [sig [(string string) -> (void)]] [flags])
(compute-composition [sig [(ptr) -> (list)] [(ptr sub-ufixnum) -> (list)]] [flags alloc])
(compute-size [sig [(ptr) -> (uint)] [(ptr sub-ufixnum) -> (uint)]] [flags alloc])
(compute-size-increments [sig [(list) -> (list)] [(list sub-ufixnum) -> (list)]] [flags alloc])
(condition-broadcast [feature pthreads] [sig [(condition-object) -> (void)]] [flags true])
(condition-continuation [sig [(continuation-condition) -> (ptr)]] [flags pure mifoldable discard])
(condition-signal [feature pthreads] [sig [(condition-object) -> (void)]] [flags true])
@ -1756,6 +1758,7 @@
($compound-condition? [flags pure unrestricted mifoldable discard])
($compute-composition [flags])
($compute-size [flags])
($compute-size-increments [flags])
($constituent? [flags])
($constituent-ports [flags])
($continuation? [flags])
@ -1997,6 +2000,7 @@
($fxvector-set-immutable! #;[sig [(fxvector) -> (ptr)]] [flags true])
($gc-cpu-time [flags true])
($gc-real-time [flags true])
($generation [flags])
($gensym->pretty-name [flags])
($guard [flags])
($hand-coded [flags])
@ -2096,6 +2100,7 @@
($map [flags])
($mark-invoked! [flags])
($maybe-compile-file [flags])
($maybe-seginfo [flags])
($noexpand? [flags])
($np-boot-code [flags])
($np-compile [flags])
@ -2162,6 +2167,8 @@
($sc-put-property! [flags])
($script [flags])
($sealed-record? [flags pure mifoldable discard])
($seginfo-generation [flags])
($seginfo-space [flags])
($set-code-byte! [flags])
($set-code-long! [flags])
($set-code-quad! [flags])
@ -2213,6 +2220,7 @@
($tc-field [flags])
($tc [flags])
($thread-list [flags])
($thread-stack-token [flags])
($thread-tc [flags])
($tlc? [flags pure unrestricted mifoldable discard])
($tlc-ht [flags mifoldable discard])

View File

@ -59,9 +59,7 @@
scheme-object))
(define weak-pair?
(foreign-procedure "(cs)s_weak_pairp"
(scheme-object)
scheme-object))
(lambda (p) (weak-pair? p)))
(define ephemeron-cons
(foreign-procedure "(cs)s_ephemeron_cons"
@ -69,9 +67,7 @@
scheme-object))
(define ephemeron-pair?
(foreign-procedure "(cs)s_ephemeron_pairp"
(scheme-object)
scheme-object))
(lambda (p) (ephemeron-pair? p)))
(define $split-continuation
(foreign-procedure "(cs)single_continuation"
@ -1483,6 +1479,7 @@
(define $close-resurrected-mutexes&conditions)
(define $tc-mutex)
(define $collect-cond)
(define get-initial-thread)
(let ()
; scheme-object's below are mutex and condition addresses, which are
; assumed to be at least ptr aligned and therefore look like fixnums
@ -1499,6 +1496,7 @@
(define cw (foreign-procedure "(cs)condition_wait" (scheme-object scheme-object scheme-object) boolean))
(define cb (foreign-procedure "(cs)condition_broadcast" (scheme-object) void))
(define cs (foreign-procedure "(cs)condition_signal" (scheme-object) void))
(define ts (foreign-procedure "(cs)threads" () scheme-object))
(define-record-type (condition $make-condition $condition?)
(fields (mutable addr $condition-addr $condition-addr-set!))
@ -1631,6 +1629,10 @@
(set! $tc-mutex ($make-mutex ($raw-tc-mutex)))
(set! $collect-cond ($make-condition ($raw-collect-cond)))
(set! get-initial-thread
(let ([thread (car (ts))])
(lambda () thread)))
))
(let ()
@ -1721,6 +1723,19 @@
(define-tlc-parameter $tlc-next $set-tlc-next!)
)
(define $generation
(lambda (x)
($generation x)))
(define $maybe-seginfo
(lambda (x)
($maybe-seginfo x)))
(define $seginfo-generation
(lambda (x)
($seginfo-generation x)))
(define $seginfo-space
(lambda (x)
($seginfo-space x)))
(define ($fxaddress x) (#3%$fxaddress x))
(define $logand