作为练习,我在Darren Wilkinson的博客文章Gibbs sampler in various languages (revisited)中重写了示例程序。
代码如下所示。此代码在我的(5岁)机器上运行大约53秒,使用SBCL 1.0.56,使用buildapp创建核心映像,然后使用
运行它time ./gibbs > gibbs.dat
由于这是如何计算帖子中其他语言的时间,我想我会做一些类似的事情 帖子中的C代码大约在25秒内运行。如果可能的话,我想尝试加速Lisp代码。
##############################
gibbs.lisp
##############################
(eval-when (:compile-toplevel :load-toplevel :execute)
(require :cl-rmath) (setf *read-default-float-format* 'double-float))
(defun gibbs (N thin)
(declare (fixnum N thin))
(declare (optimize (speed 3) (safety 1)))
(let ((x 0.0) (y 0.0))
(declare (double-float x y))
(print "Iter x y")
(dotimes (i N)
(dotimes (j thin)
(declare (fixnum i j))
(setf x (cl-rmath::rgamma 3.0 (/ 1.0 (+ (* y y) 4))))
(setf y (cl-rmath::rnorm (/ 1.0 (+ x 1.0)) (/ 1.0 (sqrt (+ (* 2 x) 2))))))
(format t "~a ~a ~a~%" i x y))))
(defun main (argv)
(declare (ignore argv))
(gibbs 50000 1000))
然后我用gibbs
调用sh gibbs.sh
构建了可执行文件gibbs.sh
##################
gibbs.sh
##################
buildapp --output gibbs --asdf-tree /usr/share/common-lisp/source/ --asdf-tree /usr/local/share/common-lisp/source/ --load-system cl-rmath --load gibbs.lisp --entry main
在使用SBCL 1.0.56进行编译时,我得到了6个编译器注释,它在下面重现。我不知道该如何对待它们,但对任何提示都会感激不尽。
; compiling file "/home/faheem/lisp/gibbs.lisp" (written 30 MAY 2012 02:00:55 PM):
; file: /home/faheem/lisp/gibbs.lisp
; in: DEFUN GIBBS
; (SQRT (+ (* 2 X) 2))
;
; note: unable to
; optimize
; due to type uncertainty:
; The result is a (VALUES (OR (DOUBLE-FLOAT 0.0) (COMPLEX DOUBLE-FLOAT))
; &OPTIONAL), not a (VALUES FLOAT &REST T).
; (/ 1.0d0 (SQRT (+ (* 2 X) 2)))
;
; note: unable to
; optimize
; due to type uncertainty:
; The second argument is a (OR (DOUBLE-FLOAT 0.0)
; (COMPLEX DOUBLE-FLOAT)), not a (COMPLEX
; DOUBLE-FLOAT).
;
; note: forced to do static-fun Two-arg-/ (cost 53)
; unable to do inline float arithmetic (cost 12) because:
; The second argument is a (OR (DOUBLE-FLOAT 0.0) (COMPLEX DOUBLE-FLOAT)), not a DOUBLE-FLOAT.
; The result is a (VALUES (OR (COMPLEX DOUBLE-FLOAT) (DOUBLE-FLOAT 0.0))
; &OPTIONAL), not a (VALUES DOUBLE-FLOAT &REST T).
; (CL-RMATH:RGAMMA 3.0d0 (/ 1.0d0 (+ (* Y Y) 4)))
;
; note: doing float to pointer coercion (cost 13)
; (SQRT (+ (* 2 X) 2))
;
; note: doing float to pointer coercion (cost 13)
; (CL-RMATH:RNORM (/ 1.0d0 (+ X 1.0d0)) (/ 1.0d0 (SQRT (+ (* 2 X) 2))))
;
; note: doing float to pointer coercion (cost 13)
;
; compilation unit finished
; printed 6 notes
; /home/faheem/lisp/gibbs.fasl written
; compilation finished in 0:00:00.073
更新1:Rainer Joswig's answer指出SQRT的论点可能是否定的,这是 是我看到的模糊编译器注释的来源,即
The result is a (VALUES (OR (DOUBLE-FLOAT 0.0) (COMPLEX DOUBLE-FLOAT))
; &OPTIONAL), not a (VALUES FLOAT &REST T).
编译器抱怨说,因为它不知道参数的值是否为正,
结果可能是一个复杂的数字。由于在示例中,值x
是与gamma分布不同的样本,
它始终大于0. Stephan在SBCL用户邮件列表中有人指出了这一点,
(参见线程"Optimizing a simple Common Lisp Gibbs sampler program"中的第二条消息,可以通过声明x大于或等于零来解决这个问题,
(declare (type (double-float 0.0 *) x))
有关相关文档,请参阅Common Lisp Hyperspec FLOAT types 和 Interval Designators
这似乎加快了代码的速度。它现在可靠地低于52秒,但仍然没有太大的收益。 这也留下了关于
的注释note: doing float to pointer coercion (cost 13)
如果出于某种原因无法修复,我想知道原因。此外,无论如何,对说明的含义的解释都会很有趣。
具体来说,pointer
这个词在这里意味着什么?这与C函数被调用的事实有关吗?此外,成本13似乎没有
很有用。测量的是什么?
此外,Rainer建议可以减少耗尽,这可能会减少运行时间。 我不知道是否可以进行减少,或者是否会减少运行时间, 但我对意见和方法感兴趣。总的来说,似乎没有太多可以改善这个功能的性能。 也许它太小而且简单。
答案 0 :(得分:4)
请注意,Common Lisp有一个THE特殊运算符。它允许您为表达式结果声明类型。例如,这允许您在可能的情况下缩小类型。
例如(SQRT somefloat)
的结果是什么?它可以是浮点数,但如果somefloat
为负数,则可能是一个复数。如果你知道somefloat总是积极的(并且只有那时),那么你可以写(the double-float (sqrt somefloat))
。然后,编译器可能能够生成更高效的代码。
另请注意,Common Lisp具有OPTIMIZE
声明。如果您想要最快的代码,您需要确保相应地设置它们。可能仅适用于个别功能。通常,它比在全球范围内改变优化要好得多。
Common Lisp有一个函数DISASSEMBLE
,可以让你查看已编译的代码。
然后是宏TIME
。您从中获得的有趣信息包括它的功能。使用双浮点算法可能会有大量的浮点数。在SBCL邮件列表上寻求帮助是有用的。也许有人可以告诉你如何避免这种情况。
答案 1 :(得分:2)
这对我有用:
(sqrt (the (double-float 0d0) (+ (* 2d0 x) 2d0)))