cs: avoid locks on struct-related tables

Avoing a lock speeds up predicates like `struct-predicate-procedure?`,
which speeds up creation of struct chaperones.
This commit is contained in:
Matthew Flatt 2020-02-08 08:20:06 -07:00
parent 1900c0e57a
commit e4c5d54e37
2 changed files with 72 additions and 58 deletions

View File

@ -90,6 +90,7 @@
(set! trigger-major-gc-allocated (* GC-TRIGGER-FACTOR post-allocated))
(set! trigger-major-gc-allocated+overhead (* GC-TRIGGER-FACTOR post-allocated+overhead)))
(update-eq-hash-code-table-size!)
(update-struct-procs-table-sizes!)
(poll-foreign-guardian)
(when (and reachable-size-increments-callback
(fx= gen (collect-maximum-generation)))

View File

@ -12,13 +12,16 @@
(define-record struct-type-prop (name guard supers))
;; Record the properties that are implemented by each rtd:
;; Record the properties that are implemented by each rtd; used
;; without a lock
(define rtd-props (make-ephemeron-eq-hashtable))
;; Maps a property-accessor function to `(cons predicate-proc can-impersonate)`:
;; Maps a property-accessor function to `(cons predicate-proc can-impersonate)`;
;; used without a lock
(define property-accessors (make-ephemeron-eq-hashtable))
;; Maps a property-predicate function to `struct-property`:
;; Maps a property-predicate function to `struct-property`; used without
;; a lock
(define property-predicates (make-ephemeron-eq-hashtable))
(define (struct-type-property? v)
@ -95,42 +98,39 @@
pv))
(do-fail fail v)))])]
[(v) (acc v default-fail)])])
(with-global-lock*
(hashtable-set! property-accessors
acc
(cons pred can-impersonate?)))
(with-global-lock*
(hashtable-set! property-predicates
pred
st))
(add-to-table! property-accessors
acc
(cons pred can-impersonate?))
(add-to-table! property-predicates
pred
st)
(values st
pred
acc)))]))
(define (struct-type-property-accessor-procedure? v)
(and (procedure? v)
(let ([v (strip-impersonator v)])
(with-global-lock* (hashtable-ref property-accessors v #f)))
#t))
(let ([v (strip-impersonator v)])
(and (#%procedure? v)
(eq-hashtable-contains? property-accessors v))))
(define/who struct-type-property-predicate-procedure?
(case-lambda
[(v) (struct-type-property-predicate-procedure? v #f)]
[(v spt)
(check who struct-type-property? :or-false spt)
(and (procedure? v)
(let* ([v (strip-impersonator v)]
[spt-c (with-global-lock* (hashtable-ref property-predicates v #f))])
(cond
[(not spt-c) #f]
[(not spt) #t]
[else (eq? spt spt-c)])))]))
(let ([v (strip-impersonator v)])
(and (#%procedure? v)
(let ([spt-c (eq-hashtable-ref property-predicates v #f)])
(cond
[(not spt-c) #f]
[(not spt) #t]
[else (eq? spt spt-c)]))))]))
(define (struct-type-property-accessor-procedure-pred v)
(car (with-global-lock (hashtable-ref property-accessors v #f))))
(car (eq-hashtable-ref property-accessors v #f)))
(define (struct-type-property-accessor-procedure-can-impersonate? v)
(cdr (with-global-lock* (hashtable-ref property-accessors v #f))))
(cdr (eq-hashtable-ref property-accessors v #f)))
(define (struct-property-ref prop rtd default)
(getprop (record-type-uid rtd) prop default))
@ -370,7 +370,8 @@
;; Records which fields of an rtd are mutable, where an rtd that is
;; not in the table has no mutable fields, and the field list can be
;; empty if a parent type is mutable:
;; empty if a parent type is mutable; this table is used without
;; a lock
(define rtd-mutables (make-ephemeron-eq-hashtable))
;; Accessors and mutators that need a position are wrapped in these records:
@ -385,69 +386,83 @@
(string->symbol (string-append (symbol->string (record-type-name rtd)) "-set!"))))
;; Register other procedures in hash tables; avoid wrapping to
;; avoid making the procedures slower
;; avoid making the procedures slower. These tables are accessed
;; without a lock, so son't use `hashtable-set!` on them.
(define struct-constructors (make-ephemeron-eq-hashtable))
(define struct-predicates (make-ephemeron-eq-hashtable))
(define struct-field-accessors (make-ephemeron-eq-hashtable))
(define struct-field-mutators (make-ephemeron-eq-hashtable))
(define struct-proc-tables-need-resize? #f)
(define (add-to-table! table key val)
(if (eq-hashtable-try-atomic-cell table key val)
(set! struct-proc-tables-need-resize? #t)
(add-to-table! table key val)))
;; Called in the collect-request handler:
(define (update-struct-procs-table-sizes!)
(when struct-proc-tables-need-resize?
(set! struct-proc-tables-need-resize? #f)
(let ([resize! (lambda (ht)
;; Since `add-to-table!` uses `eq-hashtable-try-atomic-cell`,
;; give the tabel a chance to resize
(let ([p (cons #f #f)])
(eq-hashtable-set! ht p #t)
(eq-hashtable-delete! ht p)))])
(resize! struct-constructors)
(resize! struct-predicates)
(resize! struct-field-accessors)
(resize! struct-field-mutators)
(resize! property-accessors)
(resize! property-predicates)
(resize! rtd-mutables)
(resize! rtd-props))))
(define (register-struct-constructor! p)
(add-to-table! struct-constructors p #t))
(#%$app/no-inline add-to-table! struct-constructors p #t))
(define (register-struct-predicate! p)
(add-to-table! struct-predicates p #t))
(#%$app/no-inline add-to-table! struct-predicates p #t))
(define (register-struct-field-accessor! p rtd pos)
(add-to-table! struct-field-accessors p (cons rtd pos)))
(#%$app/no-inline add-to-table! struct-field-accessors p (cons rtd pos)))
(define (register-struct-field-mutator! p rtd pos)
(add-to-table! struct-field-mutators p (cons rtd pos)))
(#%$app/no-inline add-to-table! struct-field-mutators p (cons rtd pos)))
(define (struct-constructor-procedure? v)
(and (procedure? v)
(let ([v (strip-impersonator v)])
(with-global-lock* (eq-hashtable-ref struct-constructors v #f)))))
(eq-hashtable-contains? struct-constructors v))))
(define (struct-predicate-procedure? v)
(and (procedure? v)
(let ([v (strip-impersonator v)])
(with-global-lock* (eq-hashtable-ref struct-predicates v #f)))))
(eq-hashtable-contains? struct-predicates v))))
(define (struct-accessor-procedure? v)
(and (procedure? v)
(let ([v (strip-impersonator v)])
(or (position-based-accessor? v)
(with-global-lock* (eq-hashtable-ref struct-field-accessors v #f))))
#t))
(eq-hashtable-contains? struct-field-accessors v)))))
(define (struct-mutator-procedure? v)
(and (procedure? v)
(let ([v (strip-impersonator v)])
(or (position-based-mutator? v)
(with-global-lock* (eq-hashtable-ref struct-field-mutators v #f))))
#t))
(eq-hashtable-contains? struct-field-mutators v)))))
(define (struct-accessor-procedure-rtd+pos v)
(if (position-based-accessor? v)
(cons (position-based-accessor-rtd v)
(position-based-accessor-offset v))
(with-global-lock* (eq-hashtable-ref struct-field-accessors v #f))))
(eq-hashtable-ref struct-field-accessors v #f)))
(define (struct-mutator-procedure-rtd+pos v)
(if (position-based-mutator? v)
(cons (position-based-mutator-rtd v)
(position-based-mutator-offset v))
(with-global-lock* (eq-hashtable-ref struct-field-mutators v #f))))
;; This indirection prevents the whole-program optimizer from inlining
;; the `with-glocal-lock*` expansion --- which, at the time of
;; writing, inflates the resulting code by 30%!
(define add-to-table! #f)
(define add-to-table!/done
(set! add-to-table!
(lambda (table key val)
(with-global-lock*
(hashtable-set! table key val)))))
(eq-hashtable-ref struct-field-mutators v #f)))
;; ----------------------------------------
@ -552,7 +567,7 @@
(let* ([parent-rtd* (strip-impersonator parent-rtd)]
[parent-props
(if parent-rtd*
(with-global-lock* (hashtable-ref rtd-props parent-rtd* '()))
(eq-hashtable-ref rtd-props parent-rtd* '())
'())]
[all-immutables (if (integer? proc-spec)
(cons proc-spec immutables)
@ -566,9 +581,8 @@
(if proc-spec
(cons prop:procedure props)
props))])
(with-global-lock* (hashtable-set! rtd-props rtd props)))
(with-global-lock*
(register-mutables! mutables rtd parent-rtd*))
(add-to-table! rtd-props rtd props))
(register-mutables! mutables rtd parent-rtd*)
;; Copy parent properties for this type:
(for-each (lambda (prop)
(let loop ([prop prop])
@ -665,12 +679,11 @@
(inspector-set! rtd 'prefab)
rtd])))])))
;; call with lock held
(define (register-mutables! mutables rtd parent-rtd)
(unless (and (equal? '#() mutables)
(or (not parent-rtd)
(not (hashtable-contains? rtd-mutables parent-rtd))))
(hashtable-set! rtd-mutables rtd mutables)))
(not (eq-hashtable-contains? rtd-mutables parent-rtd))))
(add-to-table! rtd-mutables rtd mutables)))
(define (check-accessor-or-mutator-index who rtd pos)
(let* ([total-count (record-type-field-count rtd)])
@ -824,7 +837,7 @@
auto-count
(make-position-based-accessor rtd* parent-total*-count (+ init-count auto-count))
(make-position-based-mutator rtd* parent-total*-count (+ init-count auto-count))
(mutables->immutables (with-global-lock* (hashtable-ref rtd-mutables rtd* '#())) init-count)
(mutables->immutables (eq-hashtable-ref rtd-mutables rtd* '#()) init-count)
next-rtd*
skipped?))])
(cond
@ -992,7 +1005,7 @@
;; ----------------------------------------
(define (struct-type-field-mutable? rtd pos)
(let ([mutables (with-global-lock* (hashtable-ref rtd-mutables rtd '#()))])
(let ([mutables (eq-hashtable-ref rtd-mutables rtd '#())])
(let loop ([j (#%vector-length mutables)])
(cond
[(fx= j 0) #f]
@ -1278,4 +1291,4 @@
(define make-name name)))])))
(define (register-struct-named! rtd)
(with-global-lock* (hashtable-set! rtd-props rtd '())))
(add-to-table! rtd-props rtd '()))