|Allegro CL version 10.1|
New since the initial 10.1 release.
A defclass-embellisher class metaobject - a subclass of
classes are described in the section Metaclasses for embellishing class
definitions in implementation.htm.
:metaclass fixed-index-class to
a defclass form causes
fixed-index assignments to be made to every direct slot in the class,
starting with the first available index after any explicitly assigned
fixed-indexes specified in either the class being defined or in any of
its superclass structure. (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 appending style of fixed-index assignment. If explicit
fixed-index assignments are not sequential, then there will be holes
in the slots vector. See also
excl:fixed-index-filling-class which fills in
index holes. If there are no explicitly assigned fixed-index slots,
then the behavior is no different between this 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 BAZ with metaclass FIXED-INDEX-CLASS (defclass baz (foo) ((x :initarg :x :reader bar-x) (y :initarg :y :reader bar-y) (z :initarg :z :reader bar-z)) (:metaclass excl:fixed-index-class)) ;; When we look at the macroexpansion of the DEFCLASS form for BAZ, we ;; see it uses indices 4, 5, and 6, leaving 0 and 2 unused: cl-user(82): (pprint (macroexpand '(defclass baz (foo) ((x :initarg :x :reader bar-x) (y :initarg :y :reader bar-y) (z :initarg :z :reader bar-z)) (:metaclass excl:fixed-index-class)))) (progn nil (eval-when (compile) (excl::check-lock-definitions-compile-time 'baz :type 'defclass (find-class 'baz nil))) (record-source-file 'baz :type :type) (excl::ensure-class-1 'baz :direct-superclasses '(foo) :direct-slots (list (list ':name 'x ':initargs '(:x) ':readers '(bar-x) 'fixed-index '4) (list ':name 'y ':initargs '(:y) ':readers '(bar-y) 'fixed-index '5) (list ':name 'z ':initargs '(:z) ':readers '(bar-z) 'fixed-index '6)) :metaclass 'fixed-index-class) (define-compiler-macro bar-x (excl::instance) (excl::bq-list `slot-value-using-class-name (excl::bq-list `quote 'baz) excl::instance (excl::bq-list `quote 'x))) ...) ;; Other accessors also have compiler macros.
The compiler macros defined for slot accessors when the
metaclass is specified cause, under the right compiler settings,
(foo-a <foo-inst>) to be
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-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):  compiler optimize space setting:  compiler optimize speed setting (3 is fastest):  3 compiler optimize debug setting (3 is maximum):  0 compiler optimize compilation-speed setting:  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 also get 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).
Copyright (c) 1998-2022, Franz Inc. Lafayette, CA., USA. All rights reserved.
This page is new in the 10.1 release.
|Allegro CL version 10.1|
New since the initial 10.1 release.