Re: master: Reduce empty hash-table struct size for default rehash params

Stas Boukarev <[email protected]> Sun, 1 Mar 2026 18:58:04 +0300
Newsgroups gmane.lisp.steel-bank.cvs,gmane.lisp.steel-bank.devel
Message-ID <CAF63=12LNqY50iGCtp7F1jD2fS8sBOb7njHBmrPE7tfDsD4pBg@mail.gmail.com>
(equalp (make-hash-table :rehash-threshold 0.3) (make-hash-table
:rehash-threshold 0.9)) => NIL

On Sun, Mar 1, 2026 at 6:06 PM snuglas via Sbcl-commits
<[email protected]> wrote:
>
> The branch "master" has been updated in SBCL:
>        via  54ea7a25b4e9a53c864cb80f445c526c2662c0e8 (commit)
>       from  083bf9756dcd9093906a6d2654027315660299f9 (commit)
>
> - Log -----------------------------------------------------------------
> commit 54ea7a25b4e9a53c864cb80f445c526c2662c0e8
> Author: Douglas Katzman <[email protected]>
> Date:   Sun Mar 1 08:44:01 2026 -0500
>
>     Reduce empty hash-table struct size for default rehash params
>
>     Now 128 bytes (down from 160 bytes) if #+compact-instance-header
> ---
>  src/code/hash-table.lisp          | 54 +++++++++++++++++++--------------------
>  src/code/target-hash-table.lisp   | 26 ++++++++++++++-----
>  src/compiler/generic/genesis.lisp |  1 -
>  3 files changed, 45 insertions(+), 36 deletions(-)
>
> diff --git a/src/code/hash-table.lisp b/src/code/hash-table.lisp
> index 12b8a0d28..d2862e2c6 100644
> --- a/src/code/hash-table.lisp
> +++ b/src/code/hash-table.lisp
> @@ -67,11 +67,8 @@
>                                  puthash-impl
>                                  remhash-impl
>                                  %hash-fun-state
> -                                test
>                                  test-fun
>                                  hash-fun
> -                                rehash-size
> -                                rehash-threshold
>                                  pairs
>                                  index-vector
>                                  next-vector
> @@ -146,28 +143,6 @@
>    ;; next GC. It may take HASH-FUN-STATE as an extra argument.
>    (hash-fun nil :type function)
>
> -  ;; The type of hash table this is. Part of the exported interface,
> -  ;; as well as needed for the MAKE-LOAD-FORM and PRINT-OBJECT methods.
> -  (test nil :type (or symbol function) :read-only t)
> -  ;; How much to grow the hash table by when it fills up. If an index,
> -  ;; then add that amount. If a floating point number, then multiply
> -  ;; it by that.
> -  (rehash-size nil :type #+c-headers-only real
> -                         #-c-headers-only (or index (single-float (1.0)))
> -               :read-only t)
> -  ;; How full the hash table has to get before we rehash
> -  ;; but only for the initial determination of how many buckets to make.
> -  ;; Subsequent resizing is at our discretion. i.e. you might think that a
> -  ;; deliberate choice of rehash size and threshold implies that you want the new
> -  ;; table to be X amount larger *and* that you care at about what load factor the
> -  ;; new table gets rehashed, but no, you don't get to pick both every time.
> -  ;; (CLHS says that these are all just "hints" and we're free to ignore)
> -  ;; If #+c-headers-only, the type is weakened to one which generates the right
> -  ;; C type without parsing a float. (NUMBER or T would result in the type being
> -  ;; lispobj, while WORD would give the struct field an incorrect name)
> -  (rehash-threshold nil :type #+c-headers-only single-float
> -                              #-c-headers-only (single-float (0.0) 1.0)
> -                        :read-only t)
>    ;; The current number of entries in the table.
>    (%count 0 :type index)
>    ;; Index into the Next vector chaining together free slots in the KV
> @@ -197,15 +172,38 @@
>                                  puthash-impl
>                                  remhash-impl
>                                  %hash-fun-state
> -                                test
> +                                %test
>                                  test-fun
>                                  hash-fun
> -                                rehash-size
> -                                rehash-threshold
> +                                %rehash-size
> +                                %rehash-threshold
>                                  pairs
>                                  index-vector
>                                  next-vector
>                                  hash-vector)))
> +  ;; The type of hash table this is. Part of the exported interface,
> +  ;; as well as needed for the MAKE-LOAD-FORM and PRINT-OBJECT methods.
> +  ;; Inferrable from FLAGS except unless a custom function.
> +  (%test nil :type (or symbol function) :read-only t)
> +  ;; How much to grow the hash table by when it fills up. If an index,
> +  ;; then add that amount. If a floating point number, then multiply
> +  ;; it by that.
> +  (%rehash-size nil :type #+c-headers-only real
> +                          #-c-headers-only (or index (single-float (1.0)))
> +               :read-only t)
> +  ;; How full the hash table has to get before we rehash
> +  ;; but only for the initial determination of how many buckets to make.
> +  ;; Subsequent resizing is at our discretion. i.e. you might think that a
> +  ;; deliberate choice of rehash size and threshold implies that you want the new
> +  ;; table to be X amount larger *and* that you care at about what load factor the
> +  ;; new table gets rehashed, but no, you don't get to pick both every time.
> +  ;; (CLHS says that these are all just "hints" and we're free to ignore)
> +  ;; If #+c-headers-only, the type is weakened to one which generates the right
> +  ;; C type without parsing a float. (NUMBER or T would result in the type being
> +  ;; lispobj, while WORD would give the struct field an incorrect name)
> +  (%rehash-threshold nil :type #+c-headers-only single-float
> +                               #-c-headers-only (single-float (0.0) 1.0)
> +                         :read-only t)
>    ;; List of (pair-index . bucket-number) which GC smashed and are almost
>    ;; equivalent to free cells, except that they are not yet unlinked from
>    ;; their chain. Skipping the removal in GC eliminates a race with REMHASH.
> diff --git a/src/code/target-hash-table.lisp b/src/code/target-hash-table.lisp
> index 7df2a7a6f..cb91b5a06 100644
> --- a/src/code/target-hash-table.lisp
> +++ b/src/code/target-hash-table.lisp
> @@ -13,8 +13,7 @@
>  (in-package "SB-IMPL")
>
>
> -(defvar *show-putweak* nil)
> -
> +(declaim (freeze-type general-hash-table))
>  (!begin-collecting-cold-init-forms)
>
>  (declaim (ftype (sfunction (hash-table t maybe-truncated-hash)
> @@ -1092,11 +1091,13 @@ Examples:
>                  (pick-table-methods (logtest flags hash-table-synchronized-flag)
>                                      (if userfunp nil test) hash-fun-state)))
>             (table
> -            (funcall (if weakp #'%alloc-general-hash-table #'%alloc-hash-table)
> -                               flags getter setter remover hash-fun-state
> -                               test test-fun hash-fun
> -                               rehash-size rehash-threshold
> -                               kv-vector index-vector next-vector hash-vector)))
> +            (if (or (/= rehash-threshold 1) (/= rehash-size default-rehash-size) userfunp weakp)
> +                (%alloc-general-hash-table flags getter setter remover hash-fun-state
> +                                           test test-fun hash-fun
> +                                           rehash-size rehash-threshold
> +                                           kv-vector index-vector next-vector hash-vector)
> +                (%alloc-hash-table flags getter setter remover hash-fun-state test-fun hash-fun
> +                                   kv-vector index-vector next-vector hash-vector))))
>        (declare (type index scaled-size))
>        ;; The trailing metadata element is either the table itself or the hash-vector
>        ;; depending on weakness. Non-weak hashing vectors can be GCed without looking
> @@ -3287,6 +3288,17 @@ table itself."
>  ;;; so its effect would just get clobbered by the defstruct.
>  (sb-kernel::assign-equalp-impl 'hash-table #'hash-table-equalp)
>
> +(defun hash-table-test (hash-table)
> +  (if (typep hash-table 'general-hash-table)
> +      (hash-table-%test hash-table)
> +      (aref #(eq eql equal equalp) (ht-flags-kind (hash-table-flags hash-table)))))
> +(defun hash-table-rehash-threshold (hash-table)
> +  (if (typep hash-table 'general-hash-table) (hash-table-%rehash-threshold hash-table) 1.0f0))
> +(defun hash-table-rehash-size (hash-table)
> +  (if (typep hash-table 'general-hash-table)
> +      (hash-table-%rehash-size hash-table)
> +      default-rehash-size))
> +
>  (!defun-from-collected-cold-init-forms !hash-table-cold-init)
>
>  #|
> diff --git a/src/compiler/generic/genesis.lisp b/src/compiler/generic/genesis.lisp
> index 5a321706f..10fd99601 100644
> --- a/src/compiler/generic/genesis.lisp
> +++ b/src/compiler/generic/genesis.lisp
> @@ -4571,7 +4571,6 @@ static inline uword_t word_has_stickymark(uword_t word) {
>                  (format stream "~&#include \"~A.h\"~%"
>                          (string-downcase (sb-vm:primitive-object-name obj)))))))
>          ;; For purposes of the C code, cast all hash tables as general_hash_table
> -        ;; even if they lack the slots for weak tables.
>          (out-to "hash-table"
>            (write-structure-type (layout-info (find-layout 'sb-impl::general-hash-table))
>                                  stream "hash_table"))
>
> -----------------------------------------------------------------------
>
>
> hooks/post-receive
> --
> SBCL
>
>
> _______________________________________________
> Sbcl-commits mailing list
> [email protected]
> https://lists.sourceforge.net/lists/listinfo/sbcl-commits


_______________________________________________
Sbcl-commits mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/sbcl-commits