对于2D图形,我需要优化我的功能,但在SBCL中我得到很多关于SBCL无法内联算术运算的注释。我尝试了各种各样的声明,但它似乎并没有使编译器感到满意。这是一个简单的例子:
(defun test-floor (x div)
(declare (type single-float x)
(type (signed-byte 64) div)
(optimize (speed 3)))
(floor x div))
给出以下4个注释。我完全失去了#' floor是一个内置函数。我试图找到关于如何在SBCL中正确地给出编译器提示的信息/教程,并且没有找到正确的信息,所以任何信息都将非常感谢!不幸的是,Common Lisp中的优化对我来说是个未知领域。我在Linux机器上使用SBCL 1.3.20。
; file: /tmp/file595dqU
; in: defun test-floor
; (FLOOR CL-FLOCKS::X CL-FLOCKS::DIV)
; --> MULTIPLE-VALUE-BIND MULTIPLE-VALUE-CALL TRUNCATE LET*
; ==>
; (SB-KERNEL:%UNARY-TRUNCATE/SINGLE-FLOAT (/ SB-C::X SB-C::F))
;
; note: forced to do full call
; unable to do inline float truncate (cost 5) because:
; The result is a (values integer &optional), not a (values
; (signed-byte 64) &rest
; t).
; --> MULTIPLE-VALUE-BIND MULTIPLE-VALUE-CALL TRUNCATE LET* VALUES - *
; ==>
; (SB-KERNEL:%SINGLE-FLOAT SB-C::RES)
;
; note: forced to do full call
; unable to do inline float coercion (cost 5) because:
; The first argument is a integer, not a (signed-byte 64).
; --> MULTIPLE-VALUE-BIND MULTIPLE-VALUE-CALL FUNCTION IF VALUES 1-
; ==>
; (- SB-C::TRU 1)
;
; note: forced to do generic-- (cost 10)
; unable to do inline fixnum arithmetic (cost 1) because:
; The first argument is a integer, not a fixnum.
; The result is a (values integer &optional), not a (values fixnum &rest t).
; unable to do inline fixnum arithmetic (cost 2) because:
; The first argument is a integer, not a fixnum.
; The result is a (values integer &optional), not a (values fixnum &rest t).
; etc.
; --> MULTIPLE-VALUE-BIND MULTIPLE-VALUE-CALL FUNCTION IF VALUES
; ==>
; (+ REM SB-C::DIVISOR)
;
; note: doing signed word to integer coercion (cost 20) from div, for:
; the second argument of generic-+
;
; compilation unit finished
; printed 4 notes
CL-USER>
答案 0 :(得分:2)
当你调用floor
时,你必须处理不同的数字子类型:代码将float除以整数(可能涉及将整数强制转换为float),然后必须强制返回结果到整数。这是必须正确完成的工作量,如果您不限制输入类型,则不太可能绕过它。
如果您使用ffloor
,那么主要结果是浮点数(当您真正需要它时,您仍然可以将其四舍五入为整数(例如转换为像素坐标))。以下代码不提供编译说明:
(defun test-floor (x div)
(declare (type single-float x)
(type fixnum div)
(optimize (speed 3)))
(ffloor x div))
您甚至可以将div
声明为float
,这将推动向调用者提供适当类型值(以及执行运行时检查)的责任。
另请注意,在定义函数之前,您应该(declaim (inline test-floor))
;这有帮助,因为编译器可以在代码中放置快捷方式,以避免检查输入参数类型和装箱结果。
编辑:
浮子的范围覆盖了一个很大的可能域(由于指数):在零附近更密集地堆积,更多地间隔到无穷大。整数值是线性间隔的,但覆盖较小的范围,具有相同的位数。因此,如果您希望保证输出适合fixnum
,则必须确保输入中的浮点数不会超出fixnum的范围。我尝试了以下方法:
(defun test-round (x)
(declare (type (single-float #.(float most-negative-fixnum 0f0)
#.(float (/ most-positive-fixnum 2) 0f0)) x)
(optimize (speed 3)))
(round x))
我必须将浮子的上限范围的一半,因为当你测试时:
(typep (round (coerce most-positive-fixnum 'single-float)) 'fixnum)
...它返回NIL。我没有太多时间来了解为什么会发生这种情况,但这取决于您的实施和架构。取最正的fixnum的一半确保该值足够低,可以转换为fixnum。现在,我没有更多的汇编说明。
(同样适用于(signed-byte 64)
))
NB。与上面的示例不同,您应该使用deftype
并避免在任何地方重复相同的声明。
答案 1 :(得分:2)
如果要指定表达式的返回值,可以使用THE
:
(the fixnum (1+ 3))
但是你真的想确保该值实际上是一个fixnum。如果你是谎言,那么Lisp可能会相信你并且你有未指定的运行时效果。 SBCL可能会在编译时发出警告,但你真的应该照顾它。如果提供的错误类型是返回错误的类型,数据可能会损坏和/或Lisp可能会崩溃。
指定返回值的另一种方法是FTYPE
声明:
例如,函数ith
可以将整数和列表作为参数。它返回任意类型 - > T
或任何子类型。
(declaim (ftype (function (integer list) t)
ith))
例如:
(the fixnum (+ (the fixnum a) (the fixnum b)))
你需要确定:
这里更容易,因为a和b的总和肯定是fixnum:
CL-USER 3 > (let ((a 3) (b 12))
(the fixnum (+ (the (integer 0 10) a)
(the (integer 3 20) b))))
15
Lisp可能会在运行时和/或编译时检查它。现在添加可以是一个简单的fixnum操作,不需要处理fixnum溢出和bignums。如果将safety
值设置为低值,则也可以省略运行时检查。但是:你永远不应该用错误的类型调用这段代码。
答案 2 :(得分:1)
SBCL声称它无法优化对floor
的调用,因为它不确定返回值是否足够小以适应64位整数。
CL-USER> (test-floor 1f25 1234)
8103727629894569426944 ;
0.0
CL-USER> (format nil “~b” *)
;; a long binary string
CL-USER> (length *)
73
可能会返回73位整数但不适合64位。
也是SBCL手册:
编辑:经过一些搜索,我找到了floor
的转换。它是here。我在下面重现:
(deftransform floor ((number divisor))
`(multiple-value-bind (tru rem) (truncate number divisor)
(if (and (not (zerop rem))
(if (minusp divisor)
(plusp number)
(minusp number)))
(values (1- tru) (+ rem divisor))
(values tru rem))))
这样就解释了编译器消息的内容