为什么在lisp中数十亿这么慢?

Seg*_*ult 8 lisp windows common-lisp while-loop

(defun billion-test () 
  (setq i 0) 
  (loop while (< i 100) do 
    (setq i (+ i 1)))) 
(billion-test) 
(print "done")
Run Code Online (Sandbox Code Playgroud)

我有上面的Lisp代码,简单地循环到十亿.问题是它真的很
慢.比我写过的任何琐碎程序都要慢.这些是
我使用的解释器运行的时间(gcl和clisp).

                       Compiled  Uncompiled
GNU Common Lisp(gcl)    270-300s  900-960s
Clisp                   280-300s  960-1647s
Run Code Online (Sandbox Code Playgroud)

我使用这个Python代码来计算时间Clisp和使用系统时间进行近似
,gcl因为您无法从命令提示符运行它.

import sys
import time
import os

start=time.time()
os.system(" ".join(sys.argv[1:]))
stop=time.time()

print "\n%.4f seconds\n"%(stop-start)
Run Code Online (Sandbox Code Playgroud)

以下是与其他语言的while循环的比较:

Kawa scheme     220.3350s
Petite chez     112.827s
C#              1.9130s
Ruby            31.045s
Python          116.8600s        113.7090s(optimized)
C               2.8240s          0.0150s(optimized)
lua             84.6970s
Run Code Online (Sandbox Code Playgroud)

我的假设是,loop while <condition> doLisp一个等价while
循环.我对这些有些怀疑1647s(25+ min),
那时我正在看东西,这可能会减慢执行速度,但几乎可以800s?我不知道.
这些结果很难相信.根据Norvig Lisp
比快3到85倍Python.从我得到了什么情况来看,最符合逻辑的
这种执行速度慢的解释是,ClispgclWindows有某种
错误的减慢大迭代.怎么,你问,我不知道?那么,我的问题是,为什么这么慢
有没有人得到这样的东西?

更新1:
我运行了Joswigs的程序并获得了以下结果:

      compiled   uncompiled
gcl    0.8s       12mins
clisp  5mins      18mins
Run Code Online (Sandbox Code Playgroud)

gcl编译程序很好,clisp但是给出了这个警告:

;; Compiling file C:\mine\.cl\test.cl ...
WARNING: in BILLION-TEST in lines 1..8 : FIXNUM-SAFETY is not a 
valid OPTIMIZE quality.
 0 errors, 1 warning
;; Wrote file C:\mine\.cl\test.fas

     ;; clisp
     [2]> (type-of 1000000000)
     (INTEGER (16777215))

     ;;gcl
     (type-of 1000000000)
      FIXNUM
Run Code Online (Sandbox Code Playgroud)

猜猜这可能是花了一分多钟的原因.

更新2:
我想我会再试一次另一个实现只是为了确认
它真的是bignum比较减慢速度.我获得sbcl
了Windows并再次运行程序:

 * (print most-positive-fixnum)
   536870911

 * (compile-file "count-to-billion.cl")
   ; compiling file "C:/mine/.cl/count-to-billion.cl"
   (written 09 OCT 2013 04:28:24 PM):
   ; compiling (DEFUN BILLION-TEST ...)
   ; file: C:/mine/.cl/count-to-billion.cl
   ; in: DEFUN BILLION-TEST
   ;     (OPTIMIZE (SPEED 3) (SAFETY 0) (DEBUG 0) (FIXNUM-SAFETY 0))
   ;
   ; caught WARNING:
   ;   Ignoring unknown optimization quality FIXNUM-SAFETY in:
   ;    (OPTIMIZE (SPEED 3) (SAFETY 0) (DEBUG 0) (FIXNUM-SAFETY 0))

 * (load "count-to-billion")
Run Code Online (Sandbox Code Playgroud)

我希望我可以告诉你需要多长时间,但我从来没有看到它的结束.我等了
2个小时,看了一集吸血鬼日记(呵呵),但还没有结束.
我期待它比Clisp它更快MOST-POSITIVE-FIXNUM,更好,更
积极.我单证员的执行慢点,因为只gcl可以拉
断不到一分钟的运行.

运行Rörd的代码gcl:

(time (loop with i = 0 while (< i 1000000000) do (incf i))) 

gcl with Rords's code:
>(load "count-to-billion.cl")
Loading count-to-billion.cl
real-time : 595.667 secs
run time : 595.667 secs

>(compile-file "count-to-billion.cl")
OPTIMIZE levels: Safety=0 (No runtime error checking), Space=0, Speed=3
Finished compiling count-to-billion.cl.
#p"count-to-billion.o"

>(load "count-to-billion")
Loading count-to-billion.o
real time : 575.567 secs
run time  : 575.567 secs
start address -T 1020e400 Finished loading count-to-billion.o
48
Run Code Online (Sandbox Code Playgroud)

更新3:

这是最后一个,我保证.我试过Rords其他代码:

(defun billion-test ()
  (loop with i fixnum = 0
        while (< i 1000000000) do (incf i)))
Run Code Online (Sandbox Code Playgroud)

令人惊讶的是,它的运行速度与Joswig一样快,不同之处在于关键字fixnumwith:

gcl的输出:

real time : 0.850 secs
run time  : 0.850 secs
Run Code Online (Sandbox Code Playgroud)

sbcl的输出(运行了大约半秒钟,然后吐出来):

debugger invoked on a TYPE-ERROR in thread
#<THREAD "main thread" RUNNING {23FC3A39}>:
  The value 536870912 is not of type FIXNUM.
Run Code Online (Sandbox Code Playgroud)

clisp的输出:

Real time: 302.82532 sec.
Run time: 286.35544 sec.
Space: 11798673420 Bytes
GC: 21413, GC time: 64.47521 sec.
NIL
Run Code Online (Sandbox Code Playgroud)

Rai*_*wig 14

  • 启动时间
  • 未声明的变量
  • 全局变量
  • 没有类型声明
  • 编译器没有告诉优化
  • 在32位机器/实现上1000000000可能不是fixnum,请参阅变量 MOST-POSITIVE-FIXNUM
  • 可能<与32位机器上的bignum相比 - >最好数到0
  • 执行缓慢

64位Common Lisp应该有更大的fixnums,我们可以使用简单的fixnum计算.

在配备2 Ghz Intel i7的MacBook Air笔记本电脑上的64位LispWorks上,我获得了未经优化的代码,可在2秒内完成.如果我们添加声明,它会更快一些.

(defun billion-test ()
  (let ((i 0))
    (declare (fixnum i)
             (optimize (speed 3) (safety 0) (debug 0))
             (inline +))
    (loop while (< i 1000000000) do 
          (setq i (+ i 1)))))


CL-USER 7 > (time (billion-test))
Timing the evaluation of (BILLION-TEST)

User time    =        0.973
System time  =        0.002
Elapsed time =        0.958
Allocation   = 154384 bytes
0 Page faults
NIL
Run Code Online (Sandbox Code Playgroud)

64位SBCL需要0.3秒.所以它更快.

使用GCL,您应该能够在32位机器上获得更好的结果.在这里,我在32位ARM处理器(Samsung Exynos 5410)上使用GCL.在ARM机器上使用GCL的十亿仍然是一个固定的.

>(type-of 1000000000)

FIXNUM

>(defun billion-test ()
  (let ((i 0))
    (declare (fixnum i)
             (optimize (speed 3) (safety 0) (debug 0))
             (inline +))
    (loop while (< i 1000000000) do 
          (setq i (+ i 1)))))

BILLION-TEST

>(compile *)

Compiling /tmp/gazonk_23351_0.lsp.
Warning:
The OPTIMIZE quality DEBUG is unknown.
End of Pass 1.  
End of Pass 2.  
OPTIMIZE levels: Safety=0 (No runtime error checking), Space=0, Speed=3
Finished compiling /tmp/gazonk_23351_0.lsp.
Loading /tmp/gazonk_23351_0.o
start address -T 0x7a36f0 Finished loading /tmp/gazonk_23351_0.o
#<compiled-function BILLION-TEST>
NIL
NIL
Run Code Online (Sandbox Code Playgroud)

现在您可以看到GCL也非常快,即使在较慢的ARM处理器上:

>(time (billion-test))

real time       :      0.639 secs
run-gbc time    :      0.639 secs
child run time  :      0.000 secs
gbc time        :      0.000 secs
NIL
Run Code Online (Sandbox Code Playgroud)

  • @Segfault:fixnum操作基本上是原始的处理器指令.一个bignum要复杂得多. (3认同)