Common lisp Common Lisp:优化SBCL中的函数

Common lisp Common Lisp:优化SBCL中的函数,common-lisp,Common Lisp,对于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个注释。我完全不知所

对于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个注释。我完全不知所措,因为#楼层是一个内置功能。我试图找到有关如何在SBCL中正确给出编译器提示的信息/教程,但没有找到正确的信息,因此任何信息都将非常感谢!不幸的是,对我来说,公共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> 

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位

Se还包括SBCL手册:

编辑:经过一些搜索,我找到了
floor
的转换。它是。我将其复制如下:

(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))))

因此,这就解释了编译器消息所说的内容,当您调用
floor
时,您必须处理不同的数字子类型:代码将一个浮点除以一个整数(这可能涉及将整数强制为浮点),然后必须将结果强制回一个整数。这是必须正确完成的工作量,如果不限制输入类型,则不太可能绕过它

如果改为使用,则主要结果是一个浮点(稍后,当您确实需要它时,您仍然可以将其舍入为整数(例如,转换为像素坐标))。以下代码不提供编译注释:

(defun test-floor (x div)
  (declare (type single-float x)
           (type fixnum div)
           (optimize (speed 3)))
  (ffloor x div))
您甚至可以将
div
声明为
float
,这将把向调用者提供适当类型的值(并执行运行时检查)的责任推给调用者

还要注意,在定义函数之前,您可能应该
(declaim(内联测试层))
;这很有帮助,因为编译器可以在代码中设置快捷方式,以避免检查输入参数类型和装箱结果

编辑:

浮点数的范围覆盖了一个很大的可能区域(由于指数):接近零的区域越密集,朝无穷远处的间隔就越大。整数值是线性间隔的,但使用相同的位数覆盖较小的范围。因此,如果您想保证输出适合于
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)
。。。它返回零。我没有太多时间来了解为什么会发生这种情况,但这取决于您的实现和体系结构。取最正的fixnum的一半可确保该值足够低,可以转换为fixnum。现在,我没有更多的汇编笔记了

(这同样适用于
(有符号字节64)


注意。与上面的示例不同,您应该使用
deftype
,并避免在任何地方重复相同的声明。

如果要指定表达式的返回值,可以使用
以下命令:

(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是一个fixnum
  • b是固定数字
  • a和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溢出和bignum。如果将
安全
值设置为较低值,则运行时检查也可能被忽略。但是:永远不要用错误的类型调用此代码。

这似乎很合理,谢谢!有没有办法告诉编译器结果永远不会超过64位范围?或者像coredump的回答中建议的那样使用ffloor会更有效吗?我没有发现任何东西yetThanks,这真的很有用!正如你正确假设的那样,我做了很多像素计算,像素存储在数组中。但是我还没有找到一种方法来对浮点进行舍入而不引起编译器的抱怨:
(defun test round(x)(declare(type single float x)(optimize(speed 3)))(round x))
编译器抱怨结果是一个整数而不是一个(有符号字节64)。如果我能告诉编译器这个值实际上在64位范围内,那就太好了。问题是:你怎么知道结果在64位范围内?如果不是这样,会发生什么?SBCL非常努力地阻止你做不正确的事情。您应该限制输入浮点域,以便结果必须在预期范围内(将编辑)。我一直在考虑限制输入浮点域,对于需要优化的函数,我所做的只是屏幕分辨率内的向量数学,因此我可以安全地假设输入值是正常的。我甚至考虑用FixNUM数学和查找表做非理性操作的一切,但我甚至不确定在当前CPU中考虑FP单元是否更快。谢谢,这也是非常有用的!虽然原则上所有这些都很清楚,但我只是不知道如何在代码中指定它,所以我