Question

In the following program, removing the line

    (declare (type (simple-array bit) arr))

makes running time increase by more than a factor of 3, using SBCL. The type information given in the defclass macro via :type on the other hand appears to have no impact on performance.

(defclass class-1 () ((arr :type (simple-array bit))))

(defun sample (inst)
  (declare (type class-1 inst))
  (let ((arr (slot-value inst 'arr)))
    (declare (type (simple-array bit) arr)) ;; 3x running time without
    (map-into arr #'(lambda (dummy) (if (< (random 1.0) 0.5) 0 1)) arr)))

(let ((inst (make-instance 'class-1)))
  (setf (slot-value inst 'arr) (make-array 10000 :element-type 'bit))
  (loop for i from 1 to 10000 do (sample inst)))

How can I have the same performance benefit without having to declare the arr slot a simple-array bit each time I use it? The latter is particularly annoying since (as far as I have found out) is requires to introduce a binding via let or similar each time; I cannot just write (slot-value inst 'arr) in the place where I need it.

Was it helpful?

Solution

First of all, this is an SBCL-specific question, you might get a better answer on the SBCL user list. Different compilers do different optimizations, and most ignore at least some declarations.

Second, you should let-bind arr because you are using it twice.

Third, you can use the if you want to avoid let-bind:

(the (simple-array bit) (slot-value inst 'arr))

Fourth, if you want the compiler to infer the type, use a specific reader instead of slot-value:

(defclass c () ((arr :type (simple-array bit) :reader c-arr)))

(defun sample (inst)
  (declare (type class-1 inst))
  (let ((arr (c-arr inst)))
    (map-into arr #'(lambda (dummy) (random 2)) arr)))

c-arr should allow the compiler to infer the value type easier, but (as you have discovered yourself!) you might need to declare its return type:

(declaim (ftype (function (c) (simple-array bit)) c-arr))

The reason is, apparently, that SBCL ignores slot type declarations.

OTHER TIPS

Adding type information has different effects depending which compiler you use and which optimization levels are in effect.

For an optimizing compiler it might look like this:

  • no information: generic operations, reasonably fast
  • type declaration available: added operations to check for this specific type during runtime -> slower
  • type declarations available, high optimization and low safety: NO added operations to type check at runtime, specialized code generated for this type -> POSSIBLY FASTER

Some compilers also ignore type declarations for CLOS slots. If they don't, there are again two variants: 1) safety means runtime checks added and 2) low safety and high speed means specialized instructions generated

Summary: type declarations may add runtime overhead with high safety due to added type checks. Specialized data types not necessarily faster with low safety and high optimization.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top