| Allegro CL version 10.1 New since the initial 10.1 release. |
A defclass-embellisher class metaobject - a subclass of
fixed-index-class
and
defclass-embellisher-class
. defclass-embellisher
classes are described in the section Metaclasses for embellishing class
definitions in implementation.htm.
Adding :metaclass fixed-index-filling-class
to
a defclass form causes
fixed-index assignments to be made to every direct slot in the class,
starting with the lowest available index not yet assigned. (Class
instances have vectors of slot values and the indices referred to are
into that vector. See Optimizing
slot-value calls with fixed indices
in implementation.htm.) This is known as
the filling style of fixed-index assignment, because it fills
holes. If there are no explicitly assigned fixed-index slots, then the
behavior is no different between this class and excl:fixed-index-class
.
Further, accessor compiler macros are defined which cause compilation of accessors to use the slot-value-using-class-name macro (which is faster than a standard accessor function) to obtain slot values.
;; Here is an example. We define a class FOO with two fixed index slots ;; using indices 1 and 3: (defclass foo () ((a :initarg :a excl:fixed-index 1 :reader foo-a) (b :initarg :b excl:fixed-index 3 :reader foo-b))) ;; Now we define a subclass bar with metaclass FIXED-INDEX-FILLING-CLASS (defclass bar (foo) ((x :initarg :x :reader bar-x) (y :initarg :y :reader bar-y) (z :initarg :z :reader bar-z)) (:metaclass excl:fixed-index-filling-class)) ;; When we look at the macroexpansion of the DEFCLASS form for BAR, we ;; see it uses indices 0, 2, and 4, which are free as FOO used 1 and 3: cl-user(80): (pprint (macroexpand '(defclass bar (foo) ((x :initarg :x :reader bar-x) (y :initarg :y :reader bar-y) (z :initarg :z :reader bar-z)) (:metaclass excl:fixed-index-filling-class)))) (progn nil (eval-when (compile) (excl::check-lock-definitions-compile-time 'bar :type 'defclass (find-class 'bar nil))) (record-source-file 'bar :type :type) (excl::ensure-class-1 'bar :direct-superclasses '(foo) :direct-slots (list (list ':name 'x ':initargs '(:x) ':readers '(bar-x) 'fixed-index '0) (list ':name 'y ':initargs '(:y) ':readers '(bar-y) 'fixed-index '2) (list ':name 'z ':initargs '(:z) ':readers '(bar-z) 'fixed-index '4)) :metaclass 'fixed-index-filling-class) (define-compiler-macro bar-x (excl::instance) (excl::bq-list `slot-value-using-class-name (excl::bq-list `quote 'bar) excl::instance (excl::bq-list `quote 'x))) ...) ;; Other accessors also have compiler macros.
The compiler macros defined for slot accessors when the
fixed-index-filling-class
metaclass is specified cause, under the right compiler settings,
forms like (foo-a <foo-inst>)
to be
transformed to (svref slot-vector
slot-fixed-index)
. This can very significantly
speed up slot accesses but part of the speedup results from discarding
the usual method processes: if the compiler macro kicks in,
:around, :before, and :after methods on the slot accessors are
ignored. Here is an example:
;;------------------- file bar-class.cl ----------------------- (in-package :user) (defclass bar () ((x :initarg :x :reader bar-x) (y :initarg :y :reader bar-y) (z :initarg :z :reader bar-z)) (:metaclass excl:fixed-index-filling-class)) (defmethod bar-x :before ((x bar)) (print "BEFORE!")) (defmethod bar-x :after ((x bar)) (print "AFTER!")) (defmethod bar-x :around ((x bar)) (print "AROUND!") (call-next-method)) (defvar *bar-inst* (make-instance 'bar :x "VALUE OF X SLOT")) (defun bbb (x) (bar-x x)) ;; ---------------------- bar-class.cl end ------------- ;; We load bar-class.cl without compiling: cl-user(2): :ld bar-class.cl ; Loading /net/gemini/home/dm/bar-class.cl ;; We call the function BBB. As expected, the :AROUND, :BEFORE ;; and :AFTER methods run and then the slot value is returned: cl-user(3): (bbb *bar-inst*) "AROUND!" "BEFORE!" "AFTER!" "VALUE OF X SLOT" ;; We set the compiler optimization values to high speed, low ;; safety and debug: cl-user(4): :opt A response of ? gets help on possible answers. compiler optimize safety setting (0 is fastest): [1] compiler optimize space setting: [1] compiler optimize speed setting (3 is fastest): [1] 3 compiler optimize debug setting (3 is maximum): [2] 0 compiler optimize compilation-speed setting: [1] 0 Compiler optimize setting is (declaim (optimize (safety 1) (space 1) (speed 3) (debug 0) (compilation-speed 0))) cl-user(5): :cf bar-class.cl ;;; Compiling file bar-class.cl ;;; Writing fasl file bar-class.fasl ;;; Fasl write complete cl-user(6): :ld bar-class.fasl ; Fast loading /net/gemini/home/dm/bar-class.fasl cl-user(7): (bbb *bar-inst*) "VALUE OF X SLOT" ;; The :AROUND, :BEFORE and :AFTER methods DO NOT RUN. ;; Just the slot value is returned. ;; There are workarounds. You can declare the accessors on ;; which you want :AROUND, :BEFORE or :AFTER methods NOTINLINE: cl-user(10): (proclaim '(notinline bar-x)) t cl-user(11): :cf bar-class.cl ;;; Compiling file bar-class.cl ;;; Writing fasl file bar-class.fasl ;;; Fasl write complete cl-user(12): :ld bar-class.fasl ; Fast loading /net/gemini/home/dm/bar-class.fasl cl-user(13): (bbb *bar-inst*) "AROUND!" "BEFORE!" "AFTER!" "VALUE OF X SLOT" cl-user(14): ;; You can get also generic function semantics with one level of ;; indirection. If you define this additional accessor: (defmethod generic-bar-a ((obj bar)) (bar-a obj)) ;; :around, :before, and :after specializers on GENERIC-BAR-A will ;; work as expected. This will not be as fast as embellished and ;; compiled BAR-A, but will generally be faster than an ;; unembellished BAR-A.
The fixed index option is designed to allow very fast slot access. Users who need to write additional methods on slot accessors should not use the fixed index embellisher classes because they should not use the compiler macros (using fixed indices is okay and itself provides some speedup).
See excl:fixed-index-class
, which start indexing
at the first available index after any explicitly assigned
fixed-indexes specified in either the class being defined or in any of
its superclass structure. In the example above, it would use indices
4, 5, and 6, leaving 0 and 2 unused.
Copyright (c) 1998-2022, Franz Inc. Lafayette, CA., USA. All rights reserved.
This page is new in the 10.1 release.
Created 2019.8.20.
| Allegro CL version 10.1 New since the initial 10.1 release. |