Re: User-defined integers: Asking for feedback
Philipp Marek via Sbcl-devel <[email protected]>
| Newsgroups | gmane.lisp.steel-bank.devel |
|---|---|
| Message-ID | <[email protected]> |
Hi everybody,
here's a newer version of my Column-Structures contrib.
> the more the interface stuff can go in the contrib itself the better,
> to address the concern of touching too many internals.
I moved quite a lot over; that also eased XC a bit.
> Maybe a better name would be user-definable immediates?
> To make it more clear that it really hooks into the
> object representation of the implementation.
I'm still not sure about this one.
This allows to define a few sets of immediates with
(distinct) integer types -- OTOH, most of the code effort
is spent on the column-structures.
Perhaps this should be two contribs - one "typed-immediates"
and one "column-structs"?
The tests pass, also the ones in the application actually using that.
It might be nice to (later?) get CLOS support, so that these types
can be used in DEFMETHOD specialization, and perhaps some more
optimizations for the accessors; and there are a few more
comments listing ideas for the future, but this should be a good
first version.
For just allocation and checking the contents,
the performance looks like this:
$ ( cd contrib/sb-udef-inttype/ && make comparison )
--- DEFSTRUCT:
Elapsed: 0:05.15 User: 4.00 System: 1.14 max RAM:1741412
--- DEF-COLUMN-STRUCT:
Elapsed: 0:06.19 User: 5.06 System: 1.12 max RAM:1363380
--- DEF-COLUMN-STRUCT with large initial-size:
Elapsed: 0:05.36 User: 4.94 System: 0.42 max RAM:895376
--- DEF-COLUMN-STRUCT with 2-level allocation:
Elapsed: 0:05.89 User: 5.45 System: 0.44 max RAM:788508
C-S is slower due to the indirections (User time is higher),
but much more GC friendly (see System time).
And the major case, dumping the heap (as an executable) shows
$ ( cd contrib/sb-udef-inttype/ && make comparison dumpfile=/tmp/bin )
--- DEFSTRUCT:
Elapsed: 0:17.57 User: 14.52 System: 3.01 max RAM:2889820
Size: 1166069352 Created: 14000000
--- DEF-COLUMN-STRUCT:
Elapsed: 0:07.88 User: 5.94 System: 1.93 max RAM:1753032
Size: 732580280 Used items: 14000001 of 14934078
--- DEF-COLUMN-STRUCT with large initial-size:
Elapsed: 0:06.97 User: 5.73 System: 1.22 max RAM:1527368
Size: 753853200 Used items: 14000001 of 15400000
--- DEF-COLUMN-STRUCT with 2-level allocation:
Elapsed: 0:07.63 User: 6.40 System: 1.22 max RAM:1496472
Size: 760671024 Used items: 14000001 of 14024704
so S-L-A-D with lots of instances only takes 1-2 seconds instead of 12.
The main usecase has >100M instances across 11 types,
so this is a major advantage on each import run.
Also, this is a _big_ advantage during runtime,
as due to the shorter (1, 2, or 4 byte) "pointers"
the (uncompressed!) image gets reduced from ~20G to ~6G --
which means faster image deployment, less RAM usage during use, etc.,
greatly offsetting the slower access patterns.
Feedback is welcome, of course!
_______________________________________________
Sbcl-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/sbcl-devel
0001-User-defined-integer-types-SBCL-basics.patch
(text/x-diff, 13.9 KB)
From cb3eae318ca02d780125245cde6f876ae2ec98ee Mon Sep 17 00:00:00 2001 From: Philipp Marek <[email protected]> Date: Thu, 3 Oct 2024 14:34:29 +0200 Subject: [PATCH 1/3] User-defined integer types: SBCL basics. --- src/code/class.lisp | 5 ++ src/code/pred.lisp | 16 +++++-- src/code/target-udef-inttype.lisp | 64 ++++++++++++++++++++++++++ src/code/typep.lisp | 4 ++ src/cold/build-order.lisp-expr | 6 ++- src/cold/exports.lisp | 8 ++++ src/compiler/fndb.lisp | 2 +- src/compiler/generic/early-objdef.lisp | 2 +- src/compiler/generic/late-objdef.lisp | 4 ++ src/compiler/generic/objdef.lisp | 3 ++ src/compiler/generic/primtype.lisp | 4 ++ src/compiler/typetran.lisp | 1 + src/compiler/x86-64/type-vops.lisp | 1 + src/pcl/precom2.lisp | 1 + src/runtime/lispobj.h | 1 + 15 files changed, 116 insertions(+), 6 deletions(-) create mode 100644 src/code/target-udef-inttype.lisp diff --git a/src/code/class.lisp b/src/code/class.lisp index e8cdf5624..a11c2cd69 100644 --- a/src/code/class.lisp +++ b/src/code/class.lisp @@ -1136,6 +1136,11 @@ between the ~A definition and the ~A definition" :inherits (integer rational real number) :codes ,sb-vm::fixnum-lowtags :prototype-form 42) + (udef-inttype + :predicate udef-inttype-p + :codes (,sb-vm::udef-inttype-lowtag) + ;:prototype-form 0 ; gets filled in later + :prototype-form sb-pcl:+slot-unbound+) (bignum :translation (and integer (not fixnum)) :inherits (integer rational real number) diff --git a/src/code/pred.lisp b/src/code/pred.lisp index fc4ac7749..2423c8c50 100644 --- a/src/code/pred.lisp +++ b/src/code/pred.lisp @@ -222,9 +222,19 @@ (if (functionp object) 'funcallable-instance 'instance))) (let* ((classoid (layout-classoid layout)) (name (classoid-name classoid))) - ;; FIXME: should the first test be (not (or (%instancep) (%funcallable-instance-p)))? - ;; God forbid anyone makes anonymous classes of generic functions. - (cond ((not (%instancep object)) + (cond ;; cold compilation already does type derivations, + ;; but sb-xc:*features* (from src/cold/shebang.lisp) isn't active? + ;#- #.(cl:find :sb-xc sb-xc:*features*) + ;#-sb-xc + ;; the predicate doesn't exist during xc + ((= sb-vm::udef-inttype-lowtag + (ldb (byte sb-vm:n-widetag-bits 0) + (sb-kernel:get-lisp-obj-address object))) + ;(udef-inttype-p object) + (sb-int:udef-inttype-type-of object)) + ;; FIXME: should the first test be (not (or (%instancep) (%funcallable-instance-p)))? + ;; God forbid anyone makes anonymous classes of generic functions. + ((not (%instancep object)) name) ((eq name 'sb-alien-internals:alien-value) `(alien ,(sb-alien-internals:unparse-alien-type diff --git a/src/code/target-udef-inttype.lisp b/src/code/target-udef-inttype.lisp new file mode 100644 index 000000000..250ffdd34 --- /dev/null +++ b/src/code/target-udef-inttype.lisp @@ -0,0 +1,64 @@ +;;;; User-defined integer types / enumerations + +;;;; This software is part of the SBCL system. See the README file for +;;;; more information. +;;;; +;;;; This software is derived from the CMU CL system, which was +;;;; written at Carnegie Mellon University and released into the +;;;; public domain. The software is in the public domain and is +;;;; provided with absolutely no warranty. See the COPYING and CREDITS +;;;; files for more information. + +(in-package "SB-INT") + +;; Apart from the bits used by udef-inttype-lowtag, +;; this here declares a further +(defconstant +udef-tag-bits+ 8) +;; bits to be used for a range of user-defined integer types. + +(defvar *udef-types* nil + "Vector of user-defined integer types. + NIL until first use.") + +(declaim (inline udef-inttype-p)) + +(defun sb-int::udef-inttype-p (x) + (= sb-vm::udef-inttype-lowtag + (ldb (byte sb-vm:n-widetag-bits 0) + (sb-kernel:get-lisp-obj-address x)))) + +;; This FTYPE gets me an error +;; 0: (ALLOCATE-CONDITION NIL) +;; 1: (MAKE-CONDITION NIL) +;; 2: (MAKE-CONDITION NIL) [more] +;; 3: (COERCE-TO-CONDITION NIL SIMPLE-ERROR ERROR) [more] +;; 4: (ERROR NIL) +;; 5: ((LABELS SB-KERNEL::RECURSE :IN CTYPEP) NIL #<BUILT-IN-CLASSOID UDEF-INTTYPE (sealed)>) +;; 6: ((LAMBDA (ELT) :IN SB-KERNEL::MEMBER-COMPLEX-SUBTYPEP-ARG1-TYPE-METHOD) NIL) +;; 7: (MAP-XSET #<FUNCTION (LAMBDA (ELT) :IN SB-KERNEL::MEMBER-COMPLEX-SUBTYPEP-ARG1-TYPE-METHOD) {12071F718B}> #S(XSET :DATA (NIL) :EXTRA 1)) +;; 8: (SB-KERNEL::MEMBER-COMPLEX-SUBTYPEP-ARG1-TYPE-METHOD #<MEMBER-TYPE NULL> #<BUILT-IN-CLASSOID UDEF-INTTYPE (sealed)>) +;; 9: (CSUBTYPEP #<MEMBER-TYPE NULL> #<BUILT-IN-CLASSOID UDEF-INTTYPE (sealed)>) +;; 10: (SB-KERNEL::%COERCE-TO-VALUES #<BUILT-IN-CLASSOID UDEF-INTTYPE (sealed)>) +;; 11: (SB-KERNEL::SIMPLIFY-UNIONS (#<MEMBER-TYPE NULL> #<BUILT-IN-CLASSOID UDEF-INTTYPE (sealed)>)) +;; 12: (SB-KERNEL::%TYPE-UNION (#<MEMBER-TYPE NULL> #<BUILT-IN-CLASSOID UDEF-INTTYPE (sealed)>)) +;; 13: (TYPE-UNION #<MEMBER-TYPE NULL> #<BUILT-IN-CLASSOID UDEF-INTTYPE (sealed)>) [more] +;; 14: (SB-C::IR1-TRANSFORM-TYPE-PREDICATE #<SB-C::LVAR 1 {1207284523}> #<BUILT-IN-CLASSOID UDEF-INTTYPE (sealed)> #<SB-C::COMBINATION :FUN #<SB-C::REF :LEAF #<SB-C::GLOBAL-VAR :%SOURCE-NAME UDEF-INTTYPE-P :TYPE #1=#<FUN-TYPE (FUNCTION (T) (VALUES BOOLEAN &OPTIONAL))> :DEFINED-TYPE #1# :WHERE-FROM :DECLARED :KIND :GLOBAL-FUNCTION {12072842F3}> {12072843D3}> :ARGS (#<SB-C::REF :%SOURCE-NAME X :LEAF #<SB-C::LAMBDA-VAR :%SOURCE-NAME X {1207283803}> {1207284573}>) {1207284443}>) +;; during cold/warm. +(declaim #+(or)(ftype (function (udef-inttype) (unsigned-byte #. +udef-tag-bits+)) + udef-inttype-tag) + (inline udef-inttype-tag)) +(defun udef-inttype-tag (x) + (ldb (byte +udef-tag-bits+ sb-vm:n-widetag-bits) + (sb-kernel:get-lisp-obj-address x))) + +(declaim (ftype (function (T) (values symbol)) udef-inttype-type-of)) +(defun udef-inttype-type-of (x) + "Returns the type symbol, or NIL." +; (declare (ignore x)) nil + ;; TODO: error out if not a udef-inttype? + (when (udef-inttype-p x) + (let ((type (and *udef-types* + (aref *udef-types* (udef-inttype-tag x))))) + (if type + type + 'udef-inttype)))) diff --git a/src/code/typep.lisp b/src/code/typep.lisp index d00f3213c..cb0435ab3 100644 --- a/src/code/typep.lisp +++ b/src/code/typep.lisp @@ -40,6 +40,10 @@ (named-type (ecase (named-type-name type) ((* t) t) + ;; cold compilation already does type derivations, + ;; but sb-xc:*features* (from src/cold/shebang.lisp) isn't active? + #- #.(cl:find :sb-xc sb-xc:*features*) + ((udef-inttype) (values (udef-inttype-p object))) ((instance) (%instancep object)) ((funcallable-instance) (funcallable-instance-p object)) ((extended-sequence) (extended-sequence-p object)) diff --git a/src/cold/build-order.lisp-expr b/src/cold/build-order.lisp-expr index 5df1afda2..7584f34d9 100644 --- a/src/cold/build-order.lisp-expr +++ b/src/cold/build-order.lisp-expr @@ -729,4 +729,8 @@ "src/code/repack-xref" #+cheneygc "src/code/purify" "src/code/module" - "src/code/save")) + "src/code/save") + +#-sb-xc + ("src/code/target-udef-inttype") + ) diff --git a/src/cold/exports.lisp b/src/cold/exports.lisp index 2cea5f65d..d4d3b71e7 100644 --- a/src/cold/exports.lisp +++ b/src/cold/exports.lisp @@ -778,6 +778,7 @@ possibly temporarily, because it might be used internally.") "CONSTANT-DISPLACEMENT" "EXTENDED-FUNCTION-DESIGNATOR" "EXTENDED-FUNCTION-DESIGNATOR-P" + ;; ..and type predicates "DOUBLE-FLOAT-P" @@ -969,6 +970,12 @@ possibly temporarily, because it might be used internally.") "STDIO-FILE" "MAKE-STDIO-FILE" + ;; for SB-UDEF-INTTYPE + + "UDEF-INTTYPE" "UDEF-INTTYPE-P" + "MAKE-UDEF-INTTYPE" "UDEF-INTTYPE-VALUE" + "UDEF-INTTYPE-TYPE-OF" + ;; for SB-COVER "*CODE-COVERAGE-INFO*" @@ -2988,6 +2995,7 @@ structure representations") "SIMPLE-FUN-NAME-SLOT" "SIMPLE-FUN-ENTRY-SAP" "FUN-POINTER-LOWTAG" + "UDEF-INTTYPE-LOWTAG" "FUNCTION-LAYOUT" "SIMPLE-FUN-INFO-SLOT" "SIMPLE-FUN-SELF-SLOT" diff --git a/src/compiler/fndb.lisp b/src/compiler/fndb.lisp index 3de9dcc22..005c7e37f 100644 --- a/src/compiler/fndb.lisp +++ b/src/compiler/fndb.lisp @@ -59,7 +59,7 @@ (defknown (null symbolp atom consp listp numberp integerp rationalp floatp complexp characterp stringp bit-vector-p vectorp simple-vector-p simple-string-p simple-bit-vector-p arrayp - packagep functionp compiled-function-p not) + packagep functionp compiled-function-p not udef-inttype-p) (t) boolean (movable foldable flushable)) (defknown (eq eql) (t t) boolean diff --git a/src/compiler/generic/early-objdef.lisp b/src/compiler/generic/early-objdef.lisp index 8662d6f50..74bc8a8aa 100644 --- a/src/compiler/generic/early-objdef.lisp +++ b/src/compiler/generic/early-objdef.lisp @@ -56,7 +56,7 @@ pad0-lowtag instance-pointer-lowtag pad1-lowtag - other-immediate-1-lowtag + udef-inttype-lowtag pad2-lowtag list-pointer-lowtag odd-fixnum-lowtag diff --git a/src/compiler/generic/late-objdef.lisp b/src/compiler/generic/late-objdef.lisp index 2d4130bae..2de947daf 100644 --- a/src/compiler/generic/late-objdef.lisp +++ b/src/compiler/generic/late-objdef.lisp @@ -156,6 +156,7 @@ (member (logand byte lowtag-mask) `(,instance-pointer-lowtag ,list-pointer-lowtag + ,udef-inttype-lowtag ,fun-pointer-lowtag ,other-pointer-lowtag)) (member byte `(#+64-bit ,single-float-widetag @@ -186,6 +187,8 @@ (dotimes (i 256) (cond ((eql 0 (logand i fixnum-tag-mask)) (setf (svref scavtab i) "immediate" (svref sizetab i) "immediate")) + ((eql udef-inttype-lowtag (logand i lowtag-mask)) + (setf (svref scavtab i) "immediate" (svref sizetab i) "immediate")) (t (let ((pointer-kind (case (logand i lowtag-mask) (#.instance-pointer-lowtag "instance") @@ -206,6 +209,7 @@ (aref sizetab #xff) "consfiller") (setf (nth instance-pointer-lowtag ptrtab) "scav_instance_pointer" (nth list-pointer-lowtag ptrtab) "scav_list_pointer" + (nth udef-inttype-lowtag ptrtab) "scav_immediate" (nth fun-pointer-lowtag ptrtab) "scav_fun_pointer" (nth other-pointer-lowtag ptrtab) "scav_other_pointer")) (dolist (entry *scav/trans/size*) diff --git a/src/compiler/generic/objdef.lisp b/src/compiler/generic/objdef.lisp index ea991e683..e0a3f7376 100644 --- a/src/compiler/generic/objdef.lisp +++ b/src/compiler/generic/objdef.lisp @@ -75,6 +75,9 @@ #+sparc (filler) (value :c-type "long double" :length #+x86 3 #+sparc 4)) +(define-primitive-object (udef-inttype :lowtag udef-inttype-lowtag + :widetag udef-inttype-lowtag)) + ;;; FIXME: the primitive-type should probably be named COMPLEX-RATIONAL ;;; but that was more invasive than renaming just the widetag (define-primitive-object (complex :type complex diff --git a/src/compiler/generic/primtype.lisp b/src/compiler/generic/primtype.lisp index f15e16b9b..9ada761b5 100644 --- a/src/compiler/generic/primtype.lisp +++ b/src/compiler/generic/primtype.lisp @@ -47,6 +47,10 @@ (!def-primitive-type signed-byte-64 (signed-reg descriptor-reg) :type (signed-byte 64)) +#+(or 64-bit 64-bit-registers) +(!def-primitive-type udef-inttype (any-reg descriptor-reg) + :type (unsigned-byte #.(- 64 n-widetag-bits))) + (define-load-time-global *fixnum-primitive-type* (primitive-type-or-lose 'fixnum)) (/show0 "primtype.lisp 53") diff --git a/src/compiler/typetran.lisp b/src/compiler/typetran.lisp index 3d25fd5d5..84ba797d0 100644 --- a/src/compiler/typetran.lisp +++ b/src/compiler/typetran.lisp @@ -355,6 +355,7 @@ (define-type-predicate consp cons) (define-type-predicate floatp float) (define-type-predicate functionp function) + (define-type-predicate udef-inttype-p udef-inttype) (define-type-predicate integerp integer) (define-type-predicate keywordp keyword) (define-type-predicate listp list) diff --git a/src/compiler/x86-64/type-vops.lisp b/src/compiler/x86-64/type-vops.lisp index 735d56821..e7d817182 100644 --- a/src/compiler/x86-64/type-vops.lisp +++ b/src/compiler/x86-64/type-vops.lisp @@ -656,6 +656,7 @@ (:generator 1 (inst cmp :byte value ,widetag))))) (define single-float-p single-float-widetag) (define characterp character-widetag) + (define udef-inttype-p udef-inttype-lowtag) (define unbound-marker-p unbound-marker-widetag)) ;;; FUNCTIONP, LISTP, %INSTANCEP, %OTHER-POINTER-P produce a flag result diff --git a/src/pcl/precom2.lisp b/src/pcl/precom2.lisp index ab6a805b2..4ee025c27 100644 --- a/src/pcl/precom2.lisp +++ b/src/pcl/precom2.lisp @@ -32,6 +32,7 @@ (precompile-ctors)))) (precompile-random-code-segments pcl)) +#+(or) (push '("SB-PCL" *built-in-classes*) *!removable-symbols*) (defun !system-class-p (x) (typep x 'sb-pcl::system-class)) diff --git a/src/runtime/lispobj.h b/src/runtime/lispobj.h index 311755f53..10a9e12a4 100644 --- a/src/runtime/lispobj.h +++ b/src/runtime/lispobj.h @@ -34,6 +34,7 @@ is_lisp_immediate(lispobj obj) #if N_WORD_BITS == 64 || (widetag == SINGLE_FLOAT_WIDETAG) #endif + || (widetag == UDEF_INTTYPE_LOWTAG) || (widetag == UNBOUND_MARKER_WIDETAG)); } -- 2.51.0
0002-Fix-GC-for-UDEFs.patch
(text/x-diff, 1 KB)
From 3d8fc2bbd4d30b6d509b6c33f1fa4f073338e2f5 Mon Sep 17 00:00:00 2001 From: Douglas Katzman <[email protected]> Date: Sun, 15 Dec 2024 17:52:09 +0100 Subject: [PATCH 2/3] Fix GC for UDEFs. --- src/runtime/lispobj.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/runtime/lispobj.h b/src/runtime/lispobj.h index 10a9e12a4..a4019f7fb 100644 --- a/src/runtime/lispobj.h +++ b/src/runtime/lispobj.h @@ -31,6 +31,7 @@ is_lisp_immediate(lispobj obj) int widetag; return (fixnump(obj) || ((widetag = header_widetag(obj)) == CHARACTER_WIDETAG) + || (widetag == UDEF_INTTYPE_LOWTAG) #if N_WORD_BITS == 64 || (widetag == SINGLE_FLOAT_WIDETAG) #endif @@ -132,6 +133,7 @@ static inline int is_cons_half(lispobj obj) if (fixnump(obj) || is_lisp_pointer(obj)) return 1; int widetag = header_widetag(obj); return widetag == CHARACTER_WIDETAG || + widetag == UDEF_INTTYPE_LOWTAG || #if N_WORD_BITS == 64 widetag == SINGLE_FLOAT_WIDETAG || #endif -- 2.51.0
0003-SB-UDEF-Inttype-contrib.patch
(text/x-diff, 113.7 KB) - not displayed