在以下程序中,删除行
(declare (type (simple-array bit) arr))
使用SBCL使运行时间增加了3倍以上。另一方面,通过:type
在defclass
宏中给出的类型信息似乎对性能没有影响。
(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)))
如果每次使用arr
插槽时都不必将其声明为simple-array bit
,我如何才能获得相同的性能优势?后者特别令人讨厌,因为(据我所知)每次都需要通过let
或类似的方式引入绑定;我不能只在需要的地方写(slot-value inst 'arr)
。
首先这是一个SBCL特定的问题,您可能会在SBCL用户列表中得到更好的答案。不同的编译器执行不同的优化,并且大多数编译器至少忽略一些声明。
第二个,您应该让绑定arr
,因为您要使用它两次。
第三,如果您想避免let绑定,可以使用the
:
(the (simple-array bit) (slot-value inst 'arr))
第四个,如果希望编译器推断类型,请使用特定的读取器,而不是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
应该允许编译器更容易地推断值类型,但(正如您自己发现的那样!)您可能需要声明其返回类型:
(declaim (ftype (function (c) (simple-array bit)) c-arr))
原因显然是SBCL忽略了槽类型声明。
添加类型信息会产生不同的效果,这取决于您使用的编译器和有效的优化级别。
对于优化编译器来说,它可能看起来像这样:
- 无信息:通用操作,速度相当快
- 可用的类型声明:添加了在运行时检查此特定类型的操作->较慢
- 类型声明可用,高度优化和低安全性:运行时不添加类型检查操作,为该类型生成专用代码->可能更快
一些编译器还忽略CLOS插槽的类型声明。如果没有,又有两种变体:1)安全意味着增加运行时检查;2)低安全和高速意味着生成的专用指令
摘要:由于添加了类型检查,类型声明可能会增加具有高安全性的运行时开销。专门的数据类型不一定速度更快,安全性低,优化程度高。