【问题标题】:optimizing simple Common Lisp gibbs sampler program优化简单的 Common Lisp gibbs 采样程序
【发布时间】:2012-06-04 12:25:38
【问题描述】:

作为练习,我重写了 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调用sh gibbs.sh来构建可执行文件gibbs

##################
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 是伽马分布的样本变量, 它总是大于 0。Stephan 在 SBCL 用户邮件列表中很有帮助地指出, (参见线程 "Optimizing a simple Common Lisp Gibbs sampler program" 中的第二条消息,这可以通过如下声明 x 大于或零来解决,

(declare (type (double-float 0.0 *) x))

请参阅 Common Lisp Hyperspec 以获取有关 FLOAT typesInterval Designators.

这似乎加快了代码速度。它现在可靠地低于 52 秒,但仍然没有太大的进步。 这也留下了关于

的注释
note: doing float to pointer coercion (cost 13)

如果由于某种原因无法修复,我想知道原因。此外,无论如何,对注释含义的解释会很有趣。 具体来说,pointer 这个词在这里是什么意思?这与调用 C 函数的事实有关吗?此外,成本 13 似乎并不 很有用。测量的是什么?

此外,Rainer 建议可能减少 consing,这可能会减少运行时间。 我不知道是否可以减少 consing,或者是否会减少运行时间, 但我会对意见和方法感兴趣。总体而言,似乎没有太多可以提高此功能的性能。 可能是太小太简单了。

【问题讨论】:

    标签: performance statistics common-lisp sbcl mcmc


    【解决方案1】:

    请注意,Common Lisp 有一个 THE 特殊运算符。它允许您声明表达式结果的类型。例如,这允许您尽可能缩小类型范围。

    例如 (SQRT somefloat) 的结果是什么?它可以是一个浮点数,但如果somefloat 为负数,它可能是一个复数。如果你知道 somefloat 总是积极的(而且只有这样),那么你可以写(the double-float (sqrt somefloat))。然后编译器可能能够生成更高效的代码。

    还要注意 Common Lisp 有 OPTIMIZE 声明。如果您想要最快的代码,您需要确保相应地设置它们。可能仅适用于个别功能。通常它比全局更改优化要非常积极。

    Common Lisp 有一个函数DISASSEMBLE,可以让你查看编译后的代码。

    然后是宏TIME。您从中获得的有趣信息包括它做了多少 consing。使用双浮点算术可能会有大量的 consing。在 SBCL 邮件列表上寻求帮助会很有用。也许有人可以告诉你如何避免这种骗局。

    【讨论】:

    • 嗨,雷纳。感谢您的评论,但我希望得到更具体的内容。我已经在使用OPTIMIZE. 我尝试使用THE,但除了在xy 分配的RHS 上之外,没有看到它在哪里有用,在这些情况下,我希望编译器可以推断出所有从双精度派生的表达式本身就是双精度。我确实尝试过,但它没有任何明显的区别。由于缺乏更好的想法,我想摆脱编译器注释,但我不明白它们在告诉我什么。
    • 我也可以尝试分析,但我不确定它对于这么小的一段代码会有多大用处。在没有任何特殊声明的情况下,我稍微超过了 1 分钟,现在我得到了大约 52 秒,所以声明没有太大区别。
    • 我仍然收到有关复数的编译器注释。进一步考虑,如果我宣布x(甚至(+ (* 2 x) 2))是积极的,也许会很高兴。有没有办法做到这一点?
    • 我收到5,902,262,600 bytes consed。这是很多骗局。 :-)
    【解决方案2】:

    这对我有用:

    (sqrt (the (double-float 0d0) (+ (* 2d0 x) 2d0)))
    

    【讨论】:

    • 这确实使 sqrt 警告消失了,但是您能补充一下吗?谢谢。
    猜你喜欢
    • 2012-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多