作者:亲家你要干啥 | 来源:互联网 | 2022-10-20 17:57
我正在使用Project Euler问题5蛮力方法来测试性能调整CL代码。我是该语言的新手,想知道如何做这种事情。我编写了一个C实现,运行时间约为1.3秒。我最初的天真CL实施大约需要15秒。
这是我的初始CL代码
(defpackage :problem-5
(:use #:cl)
(:export :divides-even-p
:has-all-divisors
:smallest-multiple)
)
(in-package :problem-5)
(defun divides-even-p (num divisor)
(= 0 (rem num divisor))
)
(defun has-all-divisors (num start stop)
(loop for divisor from start to stop do
(cond
((not (divides-even-p num divisor)) (return-from has-all-divisors nil))
)
(+ divisor 1)
)
t
)
(defun smallest-multiple (n)
(do ((multiple 1 (+ multiple 1)))
((has-all-divisors multiple 1 n) (format t "~A" multiple))
))
(time (smallest-multiple 20))
到目前为止,我所读到的技巧是1)优化速度和安全性,2)内联函数和3)显式设置变量类型。应用这些东西,我得到以下代码
(defpackage :problem-5
(:use #:cl)
(:export :divides-even-p
:has-all-divisors
:smallest-multiple)
)
(in-package :problem-5)
(declaim (optimize (speed 3) (safety 0))
(inline divides-even-p)
(inline has-all-divisors)
)
(defun divides-even-p (num divisor)
(declare (type integer num divisor))
(zerop (rem num divisor))
)
(defun has-all-divisors (num start stop)
(declare (type integer num start stop))
(loop for divisor of-type integer from start to stop do
(cond
((not (divides-even-p num divisor)) (return-from has-all-divisors nil))
)
)
t
)
(defun smallest-multiple ()
(do ((multiple 1 (+ multiple 1)))
((has-all-divisors multiple 2 20) (format t "~A" multiple))
))
(time (smallest-multiple))
现在,当我运行该版本时,它将在7秒而不是15秒内运行。2倍加速,因此方向正确。还有什么可以做以加快速度?对我来说,最明显的是最小乘数的do循环。首先,我不知道如何为多重变量指定类型。你是怎样做的?有没有更好的方法来进行开放循环,从而产生更少的代码?您将如何尝试从此处提高性能?C代码的运行时间约为1.3秒,因此我很乐意将其降低到2或3秒。
1> Svante..:
对于一个,您可以使用fixnum
代替integer
。后者包含所有整数,前者仅包含适合一个机器字的整数减去几个标记位(通常约为61或62位)。
do
循环中的声明位于主体的开头:
(do ((m 1 (1+ m)))
((has-all-divisors m 2 20)
m)
(declare (fixnum m)))
您也可以loop
在这里使用:
(loop :for m :of-type fixnum :upfrom 1
:when (has-all-divisors m 2 20)
:do (return m))
代码改进:
请不要在括号内留下指甲剪那样的痕迹。
使用if
了一个有两个分支的条件。
Loop
有一个always
关键字:
(loop :for divisor :of-type fixnum :from start :to stop
:always (divides-even-p num divisor))