[prev in list] [next in list] [prev in thread] [next in thread] 

List:       sbcl-devel
Subject:    Re: [Sbcl-devel] [Sbcl-commits] master: Optimize XSET-UNION
From:       Stas Boukarev <stassats () gmail ! com>
Date:       2023-01-05 16:46:18
Message-ID: CAF63=133fr4LBC9TxqNo9Zpf=UncT=K9S8iPRN0Rs4tGyQL-Qw () mail ! gmail ! com
[Download RAW message or body]

On windows:
::: Running :CTYPE-CACHE
Arena @ 0000000003033040: scavenging 00000000030330e0..0000000003038050
Checking threads...
fatal error encountered in SBCL pid 1012410128:
can't find interrupt context

On Thu, Jan 5, 2023 at 3:42 AM snuglas via Sbcl-commits
<sbcl-commits@lists.sourceforge.net> wrote:
>
> The branch "master" has been updated in SBCL:
>        via  9cc99c095dc5eb184506ae5356abb86e4b46cb6e (commit)
>       from  2dd4939e27783f0b20f2836ae143e3bb5f2b1777 (commit)
>
> - Log -----------------------------------------------------------------
> commit 9cc99c095dc5eb184506ae5356abb86e4b46cb6e
> Author: Douglas Katzman <dougk@google.com>
> Date:   Wed Jan 4 14:57:54 2023 -0500
>
>     Optimize XSET-UNION
>
>     Also, ensure no arena-allocation of stable MEMBER type hashes
> ---
>  src/code/xset.lisp      | 47 ++++++++++++++++++++++++++++++----------
>  tests/arena.impure.lisp | 57 +++++++++++++++++++++++++++++++++++++++++++++++++
>  tests/xset.pure.lisp    | 24 +++++++++++++++++++++
>  3 files changed, 117 insertions(+), 11 deletions(-)
>
> diff --git a/src/code/xset.lisp b/src/code/xset.lisp
> index 260a7f3a4..5f8907fb2 100644
> --- a/src/code/xset.lisp
> +++ b/src/code/xset.lisp
> @@ -81,16 +81,6 @@
>                        (xset-data xset) table))))
>          (setf (gethash elt data) t))))
>
> -(defun xset-union (a b)
> -  (let ((xset (alloc-xset)))
> -    (map-xset (lambda (x)
> -                (add-to-xset x xset))
> -              a)
> -    (map-xset (lambda (y)
> -                (add-to-xset y xset))
> -              b)
> -    xset))
> -
>  (defun xset-member-p (elt xset)
>    (let ((data (xset-data xset)))
>      (if (if (listp data)
> @@ -110,7 +100,14 @@
>                     data)
>            members))))
>
> +;;; Possible TODO:
> +;;; INTERSECTION and UNION could allocate the new xset where the input(s)
> +;;; were, to avoid an extra copy operation.
> +;;; The reason for not always forcing to dynamic space is that I'd like
> +;;; (eventually) the compiler to able to run in an arena, with only its
> +;;; output forced to the dynamic or immmobile space.
>  (defun xset-intersection (a b)
> +  (declare (inline alloc-xset))
>    (let ((intersection (alloc-xset)))
>      ;; Under the assumption that lookup time is constant in either set,
>      ;; you should scan the * smaller * set to see if each item
> @@ -131,6 +128,33 @@
>                   source)))
>      intersection))
>
> +;;; This attempts to return A or B if one is a subset of the other.
> +(defun xset-union (a b)
> +  (declare (inline !new-xset))
> +  (binding* (((small large)
> +              (if (< (xset-count a) (xset-count b)) (values a b) (values b a)))
> +             (data (xset-data large)))
> +  ;; If one of A or B is a hash-table, then surely it's the larger. The other might be too.
> +  ;; For each key in the smaller, count the elements missing from the larger.
> +  (if (hash-table-p data)
> +      (let ((missing 0))
> +        (declare (index missing))
> +        (map-xset (lambda (x) (unless (gethash x data) (incf missing))) small)
> +        (cond ((= missing 0) large)
> +              (t
> +               ;; We have an exact count for the resulting hash-table.
> +               (let ((new (make-hash-table :size (+ (hash-table-count data) missing))))
> +                 ;; Ideally we would have a valueless hash-table so we don't store all the Ts
> +                 (map-xset (lambda (k) (setf (gethash k new) t)) small)
> +                 (maphash (lambda (k v) (setf (gethash k new) v)) data)
> +                 (!new-xset new 0)))))
> +      ;; Both A and B are lists. Share list tails since the lists are immutable.
> +      ;; Let the resulting XSET dynamically become a hash-table if it wants to.
> +      (let ((xset (!new-xset data (xset-count large))))
> +        (dolist (elt (xset-data small)) (add-to-xset elt xset))
> +        ;; if nothing was actually added, then NEW can be GCed
> +        (if (eq (xset-data xset) data) large xset)))))
> +
>  (defun xset-subset-p (xset1 xset2)
>    (when (<= (xset-count xset1) (xset-count xset2))
>      (let ((data (xset-data xset2)))
> @@ -214,7 +238,8 @@
>  (define-load-time-global *xset-stable-hashes* (make-hash-table :test 'eq))
>
>  (defun xset-generate-stable-hashes (xset &aux (hashmap *xset-stable-hashes*))
> -  #-sb-xc-host (declare (notinline sb-impl::eql-hash)) ; forward ref
> +  #-sb-xc-host (declare (notinline sb-impl::eql-hash) ; forward ref
> +                        (sb-c::tlab :system))
>    (flet ((get-stable-hash-cell (obj)
>             (let ((cell (gethash obj hashmap)))
>               (cond (cell
> diff --git a/tests/arena.impure.lisp b/tests/arena.impure.lisp
> index aa05a3624..03e09709b 100644
> --- a/tests/arena.impure.lisp
> +++ b/tests/arena.impure.lisp
> @@ -354,6 +354,8 @@
>  ;;; except that the memory is still there so we can figure out what went wrong
>  ;;; with user code. This might pass on #+-linux but has not been tested.
>  (test-util:with-test (:name :arena-use-after-free :skipped-on (:not :linux))
> +  ;; scary messages scare me
> +  (format t "::: NOTE: Expect a \"CORRUPTION WARNING\" from this test~%")
>    (hide-arena *another-arena*)
>    (let (caught)
>      (block foo
> @@ -371,6 +373,61 @@
>    (rewind-arena *another-arena*)
>    (dotimes (i 10) (f *another-arena* 1000)))
>
> +;;;; Type specifier parsing and operations
> +
> +(defparameter *bunch-of-objects*
> +  `((foo)
> +    "astring"
> +    #*1010
> +    ,(find-package "CL")
> +    ,(pathname "/tmp/blub")
> +    ,#'open
> +    #2a((1 2) (3 4))
> +    ,(ash 1 64)
> +    ))
> +
> +;; These type-specs are themselves consed so that we can
> +;; ascertain whether there are arena pointers in internalized types.
> +(defun get-bunch-of-type-specs ()
> +  `((integer ,(random 47) *)
> +    (and bignum (not (eql ,(random 1000))))
> +    (and bignum (not (eql ,(logior #x8000000000000001
> +                                   (ash (1+ (random #xF00)) 10)))))
> +    (member ,(complex (coerce (random 10) 'single-float)
> +                      (coerce (- (random 10)) 'single-float))
> +            (goo)
> +            #\thumbs_up_sign)
> +    (or stream (member #\thumbs_down_sign :hello))
> +    (array t (,(+ 10 (random 10))))))
> +
> +(defun show-cache-counts ()
> +  (dolist (s sb-impl::*cache-vector-symbols*)
> +    (let ((v (symbol-value s)))
> +      (when (vectorp v)
> +        (format t "~5d  ~a~%"
> +                (count-if (lambda (x) (not (eql x 0))) v)
> +                s)))))
> +
> +(defun ctype-operator-tests (arena &aux (result 0))
> +  (sb-int:drop-all-hash-caches)
> +  (flet ((try (spec)
> +           (dolist (x *bunch-of-objects*)
> +             (when (typep x spec)
> +               (incf result)))))
> +    (sb-vm:with-arena (arena)
> +      (let ((specs (get-bunch-of-type-specs)))
> +        (dolist (spec1 specs)
> +          (dolist (spec2 specs)
> +            (try `(and ,spec1 ,spec2))
> +            (try `(or ,spec1 ,spec2))
> +            (try `(and ,spec1 (not ,spec2)))
> +            (try `(or ,spec1 (not ,spec2))))))))
> +  (assert (null (sb-vm:c-find-heap->arena arena)))
> +  result)
> +(test-util:with-test (:name :ctype-cache)
> +  (let ((arena (sb-vm:new-arena 1048576)))
> +    (ctype-operator-tests arena)))
> +
>  ;; #+sb-devel preserves some symbols that the test doesn't care about
>  ;; as the associated function will never be called.
>  (defvar *ignore* '("!EARLY-LOAD-METHOD"))
> diff --git a/tests/xset.pure.lisp b/tests/xset.pure.lisp
> index 44eff24cf..0c68fe335 100644
> --- a/tests/xset.pure.lisp
> +++ b/tests/xset.pure.lisp
> @@ -12,3 +12,27 @@
>          (assert (sb-int:xset= a b))
>          (assert (= (sb-int:xset-elts-hash a)
>                     (sb-int:xset-elts-hash b)))))))
> +
> +(with-test (:name :xset-fast-union)
> +  (let ((s1 (sb-int:alloc-xset))
> +        (s2 (sb-int:alloc-xset)))
> +    (sb-int:add-to-xset #\a s1)
> +    (sb-int:add-to-xset #\b s1)
> +    (sb-int:add-to-xset #\c s1)
> +    (assert (eq (sb-int:xset-union s1 s2) s1))
> +    (assert (eq (sb-int:xset-union s2 s1) s1))
> +    (sb-int:add-to-xset #\b s2)
> +    (assert (eq (sb-int:xset-union s1 s2) s1))
> +    (assert (eq (sb-int:xset-union s2 s1) s1)))
> +  (let ((s1 (sb-int:alloc-xset))
> +        (s2 (sb-int:alloc-xset)))
> +    (loop for i from (char-code #\a) to (char-code #\z)
> +          do (sb-int:add-to-xset (code-char i) s1))
> +    (sb-int:add-to-xset #\x s2)
> +    (loop for i from 1 to 10
> +          do (sb-int:add-to-xset (code-char i) s1))
> +    (assert (listp (sb-kernel::xset-data s2)))
> +    (let ((union1 (sb-int:xset-union s1 s2)))
> +      (assert (= (sb-int:xset-count union1) (+ 26 10)))
> +      (let ((union2 (sb-int:xset-union s2 s1))) ; had better commute
> +        (assert (sb-int:xset= union1 union2))))))
>
> -----------------------------------------------------------------------
>
>
> hooks/post-receive
> --
> SBCL
>
>
> _______________________________________________
> Sbcl-commits mailing list
> Sbcl-commits@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/sbcl-commits


_______________________________________________
Sbcl-devel mailing list
Sbcl-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sbcl-devel
[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic