ClassPackage: exclToCDocOverviewCGDocRelNotesFAQIndexPermutedIndex
Allegro CL version 10.1
New since the initial 10.1 release.

fixed-index-filling-class

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.

Warning: code containing accessor calls may ignore :before, :around, and :after methods

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.

ToCDocOverviewCGDocRelNotesFAQIndexPermutedIndex
Allegro CL version 10.1
New since the initial 10.1 release.