我是 Common Lisp 的新手,遇到了一个让我觉得很奇怪的性能问题。我在循环中使用 rem 检查一个数字是否可以被 10 整除。如果我将支票移动到一个函数中,它的运行速度会慢 5 倍。什么会导致这种情况?
我在 64 位 Ubuntu 18.04 上运行 sbcl 1.4.5。
(defun fn (x)
(= 0 (rem x 10))
)
(defun walk-loop-local (n)
(loop for i from 1 to n do
(= 0 (rem i 10))
))
(defun walk-loop-func (n)
(loop for i from 1 to n do
(fn i)
))
(time (walk-loop-local 232792560))
(time (walk-loop-func 232792560))
我希望时间是相同的(而且要快得多,但这是一个单独的问题)。相反,这是输出,
CL-USER> (load "loops.lisp")
Evaluation took:
0.931 seconds of real time
0.931389 seconds of total run time (0.931389 user, 0.000000 system)
100.00% CPU
2,414,050,454 processor cycles
0 bytes consed
Evaluation took:
4.949 seconds of real time
4.948967 seconds of total run time (4.948967 user, 0.000000 system)
100.00% CPU
12,826,853,706 processor cycles
0 bytes consed
最佳答案
您正在使用 SBCL 编译器:
(defun walk-loop-local (n)
(loop for i from 1 to n do
(= 0 (rem i 10))))
我认为您的代码在循环迭代中什么都不做。它被优化掉了,因为
=
的值form 不会在任何地方使用,也没有副作用。因此没有开销,因为没有代码。
使用
(disassemble #'walk-local-form)
检查编译代码。If I move the check into a function, it runs 5x slower. What would cause that?
不是什么都不做,而是在每次迭代中调用该函数并执行您的代码。
实际测量调用开销
(defparameter *i* nil)
(defun walk-loop-local (n)
(loop for i from 1 to n do
(setf *i* (= 0 (rem i 10)))))
(defun fn (x)
(setf *i* (= 0 (rem x 10))))
(defun walk-loop-func (n)
(loop for i from 1 to n do
(fn i)))
在上述情况下,代码不会被删除。
CL-USER> (time (walk-loop-local 232792560))
Evaluation took:
5.420 seconds of real time
5.412637 seconds of total run time (5.399134 user, 0.013503 system)
99.87% CPU
6,505,078,020 processor cycles
0 bytes consed
NIL
CL-USER> (time (walk-loop-func 232792560))
Evaluation took:
6.235 seconds of real time
6.228447 seconds of total run time (6.215409 user, 0.013038 system)
99.89% CPU
7,481,974,847 processor cycles
0 bytes consed
您可以看到函数调用开销并没有那么大。
关于common-lisp - Common Lisp SBCL 中函数调用的性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56624200/