Lisp SBCL&x27;s statistic profiler不会为每个调用的函数显示一个条目

Lisp SBCL&x27;s statistic profiler不会为每个调用的函数显示一个条目,lisp,common-lisp,profiler,sbcl,Lisp,Common Lisp,Profiler,Sbcl,我正在使用SBCL的统计分析器分析这些功能: (defun fact-rec (n) (if (zerop n) 1 (* n (fact-rec (1- n))))) (defun fact-call (n) (fact-rec n)) (defun fact-iter (n) (loop :with accu = 1 :for i :upfrom 2 :to n :doing (setf accu (* i accu)) :fi

我正在使用SBCL的统计分析器分析这些功能:

(defun fact-rec (n)
  (if (zerop n)
      1
      (* n (fact-rec (1- n)))))

(defun fact-call (n)
  (fact-rec n))

(defun fact-iter (n)
  (loop :with accu = 1
    :for i :upfrom 2 :to n
    :doing (setf accu (* i accu))
    :finally (return accu)))

(defun fact-opti-iter (n)
  (let ((accu 1))
    (tagbody
     loop
       (unless (zerop n)
         (setf accu (* n accu))
         (decf n)
         (go loop)))
    accu))
为了评估递归版本的权重,我定义了一个函数
fact call
,它位于所有
fact rec
调用下面的堆栈上,以便能够正确地监视它。以下是我的分析代码:

(sb-sprof:profile-call-counts 'fact-rec 'fact-call 'fact-iter 'fact-opti-iter)
(sb-sprof:with-profiling (:max-samples 1000
                   :loop nil
                   :report :flat)
       (dotimes (i 1500)
         (fact-call i)
         (fact-iter i)
         (fact-opti-iter i)))
这样做可以确保从不直接调用
事实记录
,因此,如果它出现在探查器的报告中,那么它必然被
事实记录
调用。但我得到的报告如下:

Profiler sample vector full (70 traces / 10000 samples), doubling the size Profiler sample vector full (133 traces / 20000 samples), doubling the size Number of samples: 195 Sample interval: 0.01 seconds Total sampling time: 1.9499999 seconds Number of cycles: 0 Sampled threads: # Self Total Cumul Nr Count % Count % Count % Calls Function ------------------------------------------------------------------------ 1 193 99.0 193 99.0 193 99.0 - SB-BIGNUM:MULTIPLY-BIGNUM-AND-FIXNUM 2 1 0.5 195 100.0 194 99.5 - SB-KERNEL:TWO-ARG-* 3 1 0.5 1 0.5 195 100.0 - SB-BIGNUM::%NORMALIZE-BIGNUM 4 0 0.0 195 100.0 195 100.0 - "Unknown component: #x100317AB30" 5 0 0.0 195 100.0 195 100.0 - SB-INT:SIMPLE-EVAL-IN-LEXENV 6 0 0.0 195 100.0 195 100.0 - EVAL 7 0 0.0 195 100.0 195 100.0 - SWANK::EVAL-REGION 8 0 0.0 195 100.0 195 100.0 - (LAMBDA NIL :IN SWANK-REPL::REPL-EVAL) 9 0 0.0 195 100.0 195 100.0 - SWANK-REPL::TRACK-PACKAGE 10 0 0.0 195 100.0 195 100.0 - SWANK::CALL-WITH-RETRY-RESTART 11 0 0.0 195 100.0 195 100.0 - SWANK::CALL-WITH-BUFFER-SYNTAX 12 0 0.0 195 100.0 195 100.0 - SWANK-REPL::REPL-EVAL 13 0 0.0 195 100.0 195 100.0 - SWANK:EVAL-FOR-EMACS 14 0 0.0 195 100.0 195 100.0 - SWANK::PROCESS-REQUESTS 15 0 0.0 195 100.0 195 100.0 - (LAMBDA NIL :IN SWANK::HANDLE-REQUESTS) 16 0 0.0 195 100.0 195 100.0 - SWANK/SBCL::CALL-WITH-BREAK-HOOK 17 0 0.0 195 100.0 195 100.0 - (FLET SWANK/BACKEND:CALL-WITH-DEBUGGER-HOOK :IN "/Users/vleo/quicklisp/dists/quicklisp/software/slime-v2.19/swank/sbcl.lisp") 18 0 0.0 195 100.0 195 100.0 - SWANK::CALL-WITH-BINDINGS 19 0 0.0 195 100.0 195 100.0 - SWANK::HANDLE-REQUESTS 20 0 0.0 195 100.0 195 100.0 - (LABELS SWANK/SBCL::RUN :IN SWANK/BACKEND:ADD-FD-HANDLER) 21 0 0.0 195 100.0 195 100.0 - SB-IMPL::SUB-SUB-SERVE-EVENT 22 0 0.0 195 100.0 195 100.0 - SB-IMPL::SUB-SERVE-EVENT 23 0 0.0 195 100.0 195 100.0 - SB-SYS:WAIT-UNTIL-FD-USABLE 24 0 0.0 195 100.0 195 100.0 - SB-IMPL::REFILL-INPUT-BUFFER 25 0 0.0 195 100.0 195 100.0 - SB-IMPL::INPUT-CHAR/UTF-8 26 0 0.0 195 100.0 195 100.0 - (LAMBDA (&REST REST) :IN SB-IMPL::GET-EXTERNAL-FORMAT) 27 0 0.0 195 100.0 195 100.0 - READ-CHAR 28 0 0.0 195 100.0 195 100.0 - SB-IMPL::%READ-PRESERVING-WHITESPACE 29 0 0.0 195 100.0 195 100.0 - READ 30 0 0.0 195 100.0 195 100.0 - SB-IMPL::REPL-READ-FORM-FUN 31 0 0.0 195 100.0 195 100.0 - SB-IMPL::REPL-FUN 32 0 0.0 195 100.0 195 100.0 - (LAMBDA NIL :IN SB-IMPL::TOPLEVEL-REPL) 33 0 0.0 195 100.0 195 100.0 - SB-IMPL::%WITH-REBOUND-IO-SYNTAX 34 0 0.0 195 100.0 195 100.0 - SB-IMPL::TOPLEVEL-REPL 35 0 0.0 195 100.0 195 100.0 - SB-IMPL::TOPLEVEL-INIT 36 0 0.0 195 100.0 195 100.0 - (FLET #:WITHOUT-INTERRUPTS-BODY-74 :IN SAVE-LISP-AND-DIE) 37 0 0.0 195 100.0 195 100.0 - (LABELS SB-IMPL::RESTART-LISP :IN SAVE-LISP-AND-DIE) 38 0 0.0 69 35.4 195 100.0 1500 FACT-OPTI-ITER 39 0 0.0 65 33.3 195 100.0 1125750 FACT-REC 40 0 0.0 61 31.3 195 100.0 1500 FACT-ITER ------------------------------------------------------------------------ 0 0.0 elsewhere 探查器样本向量已满(70道/10000个样本),大小加倍 探查器样本向量已满(133道/20000个样本),大小加倍 样本数目:195 采样间隔:0.01秒 总采样时间:1.949999秒 循环次数:0 采样线程: # 自总积云 Nr Count%Count%Count%调用函数 ------------------------------------------------------------------------ 119399.019399.019399.019399.0-SB-BIGNUM:MULTIPLY-BIGNUM-AND-FIXNUM 21 0.5 195 100.0 194 99.5-SB-KERNEL:TWO-ARG-* 3 1 0.5 1 0.5 195 100.0-SB-BIGNUM::%NORMALIZE-BIGNUM 4 0 0.0 195 100.0 195 100.0-“未知部件:#x100317AB30” 5 0 0.0 195 100.0 195 100.0-SB-INT:SIMPLE-EVAL-IN-lexev 6 0 0.0 195 100.0 195 100.0-评估 7 0 0.0 195 100.0 195 100.0-SWANK::EVAL-REGION 8 0 0.0 195 100.0 195 100.0-(λ零:在SWANK-REPL::REPL-EVAL中) 9 0 0.0 195 100.0 195 100.0-SWANK-REPL::TRACK-PACKAGE 10 0 0.0 195 100.0 195 100.0-SWANK::CALL-WITH-RETRY-RESTART 11 0 0.0 195 100.0 195 100.0-SWANK::CALL-WITH-BUFFER-SYNTAX 12 0 0.0 195 100.0 195 100.0-SWANK-REPL::REPL-EVAL 13 0 0.0 195 100.0 195 100.0-SWANK:EVAL-FOR-EMACS 14 0 0.0 195 100.0 195 100.0-SWANK::PROCESS-REQUESTS 15 0 0.0 195 100.0 195 100.0-(LAMBDA NIL:IN-SWANK::HANDLE-REQUESTS) 16 0 0.0 195 100.0 195 100.0-SWANK/SBCL::带断钩呼叫 17 0 0.0 195 100.0 195 100.0-(FLET-SWANK/后端:CALL-WITH-DEBUGGER-HOOK:IN”/Users/vleo/quicklisp/dists/quicklisp/software/slime-v2.19/SWANK/sbcl.lisp) 18 0 0.0 195 100.0 195 100.0-SWANK::带绑定的调用 19 0 0.0 195 100.0 195 100.0-SWANK::HANDLE-REQUESTS 20 0 0.0 195 100.0 195 100.0-(标签SWANK/SBCL::RUN:IN SWANK/BACKEND:ADD-FD-HANDLER) 21 0 0.0 195 100.0 195 100.0-SB-IMPL::SUB-SUB-SERVE-EVENT 22 0 0.0 195 100.0 195 100.0-SB-IMPL::SUB-SERVE-EVENT 23 0 0.0 195 100.0 195 100.0-SB-SYS:WAIT-UNTIL-FD-Available 24 0 0.0 195 100.0 195 100.0-SB-IMPL::重新填充输入缓冲区 25 0 0.0 195 100.0 195 100.0-SB-IMPL::INPUT-CHAR/UTF-8 26 0 0.0 195 100.0 195 100.0-(LAMBDA(&REST):在SB-IMPL::GET-EXTERNAL-FORMAT中) 27 0 0.0 195 100.0 195 100.0-可读字符 28 0 0.0 195 100.0 195 100.0-SB-IMPL::%保留读取的空白 29 0 0.0 195 100.0 195 100.0-读取 30 0 0.0 195 100.0 195 100.0-SB-IMPL::REPL-READ-FORM-FUN 31 0 0.0 195 100.0 195 100.0-SB-IMPL::REPL-FUN 32 0 0.0 195 100.0 195 100.0-(LAMBDA NIL:在SB-IMPL::TOPLEVEL-REPL中) 33 0 0.0 195 100.0 195 100.0-SB-IMPL::%带-response-IO-SYNTAX 34 0 0.0 195 100.0 195 100.0-SB-IMPL::TOPLEVEL-REPL 35 0 0.0 195 100.0 195 100.0-SB-IMPL::TOPLEVEL-INIT 36 0 0.0 195 100.0 195 100.0-(FLET#:无中断-BODY-74:在SAVE-LISP-AND-DIE中) 37 0 0.0 195 100.0 195 100.0-(标签SB-IMPL::RESTART-LISP:IN SAVE-LISP-AND-DIE) 38 0 0.0 69 35.4 195 100.0 1500 FACT-OPTI-ITER 39 0 0.0 65 33.3 195 100.0 1125750事实记录 40 0.0 61 31.3 195 100.0 1500 FACT-ITER ------------------------------------------------------------------------ 0.0其他地方 没有提到事实调用,即使它确实被调用过。此外,还有一个条目用于
事实记录
。如果
事实调用
的调用在堆栈中较深,并且记录了
事实记录
,是否也应该记录


谢谢

您确定对
事实调用
的调用保持在堆栈上吗?你查过了吗

SBCL编译器可以执行TCO(尾部调用优化)。函数
fact call
在尾部位置调用
fact rec
。此函数调用可以替换为跳转,重用当前堆栈帧

TCO

让我们修改
fact rec
,以便它调用
break
,我们可以查看堆栈:

(defun fact-rec (n)
  (if (zerop n)
      (progn (break) 1)
    (* n (fact-rec (1- n)))))

(defun fact-call (n)
  (fact-rec n))       ; tail call to FACT-REC
让我们从
test
调用它:

(defun test ()
 (fact-call 4))
回溯看起来像这样-no
事实调用

Backtrace:
  0: (FACT-REC 0)
  1: (FACT-REC 1)
  2: (FACT-REC 2)
  3: (FACT-REC 3)
  4: (FACT-REC 4)
无TCO

现在我们告诉SBCL编译器不要在事实调用中使用TCO:

(defun fact-call (n)
  (declare (optimize (debug 3)))    ; debug level 3 does no TCO
  (fact-rec n))
现在这是调用堆栈:

Backtrace:
  0: (FACT-REC 0)
  1: (FACT-REC 1)
  2: (FACT-REC 2)
  3: (FACT-REC 3)
  4: (FACT-REC 4)
  5: (FACT-CALL 4)
如您所见,对
FACT-call
的调用保持在堆栈上