如何优化这段 Racket 代码?

5

我想要计算1 + 1/2 + 1/3 + ... + 1/100000000的和(使用双精度浮点数)。

在SBCL中,这段代码的运行速度与C语言一样快:

(loop for i fixnum from 1 to 100000000 sum (/ 1.0d0 i) double-float)

我该如何在Typed Racket中优化这段代码?我尝试过:
#lang typed/racket

(define: (test) : Float
         (for/fold: : Float
                    ([s : Float 0.0])
                    ([i : Fixnum (in-range 1 100000001)])
                    (+ s (/ 1.0 i))))

(time (test))

这段代码只比未经类型定义的代码快了一点。我能做得更好吗?


4
一个快速的建议是尝试使用 optimization-coach - Greg Hendershott
2个回答

7
如果你按照Greg的建议在此运行优化教练,它会立即告诉你循环体缓慢的原因是/函数正在执行混合算术(在fixnum和flonum上)。如果你在i的位置插入(fx->fl i),它会更快(在我的机器上接近2倍)。
另外,如果你在DrRacket中计时,请使用racket可执行文件计时。DrRacket添加了有助于开发的调试工具,但不适用于计时。

4
使用 exact->inexactunsafe-fx->fl 而不是 fx->fl 可以再次提高约30%的速度,因为它省略了检查参数是否为固定数的步骤。 - stchang
4
一些预备性的基准测试表明,对于这个程序来说,经过优化的 Typed Racket 代码比 SBCL 快大约10%。 - stchang

2

这是一个新版本,在其中我创建了一个小助手宏来对浮点数求和。

#lang typed/racket

(require syntax/parse/define)

(define-simple-macro (for/flsum x ... (c ...) b ... e)
  (for/fold : Float x ... ([s 0.0]) (c ...) b ... (+ s e)))

(time (for/flsum ([i : Positive-Fixnum (in-range 1 100000001)]) (/ 1.0 i)))

请注意,使用Positive-Fixnum作为类型意味着我们不需要任何额外的转换--Typed Racket知道i永远不会为0,因此/可以始终被优化。在我的机器上,现在运行速度几乎与SBCL一样快。
它和SBCL代码之间唯一的区别是需要指定fixnum为正数,这是必需的,因为SBCL对于(/ 1.0 0)(/ 1.0 0.0)具有相同的语义--它会引发异常,而Racket在第二种情况下产生+inf.0,在第一种情况下产生异常。
我计划将for/flsum或类似的东西添加到Racket本身中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接