Common Lisp - Consing 优化问题 - 循环和 &rest 参数

Common Lisp - Consing Optimization question - loops and &rest parameters

我已经使用 Common Lisp 完成了一种语言的实现,我正在寻求优化它,因为使用 Lisp 需要大约 1400 秒而不是 Java 中的大约 72 秒。 (此处代码 cl-lox)。

我启动了探查器并得到了这个头号罪魁祸首:

  seconds  |     gc     |     consed     |    calls    |  sec/call  |  name  
------------------------------------------------------------------
    32.879 |      0.000 |              0 | 104,512,464 |   0.000000 | LOX.INTERPRETER::LOOKUP-VARIABLE
     6.395 |      0.062 |  1,162,823,904 |  29,860,705 |   0.000000 | LOX.CALLABLE:LOX-CALLABLE-ARITY
     6.314 |      0.139 |  2,442,330,208 |  74,651,757 |   0.000000 | LOX.INTERPRETER::TYPE?
     5.220 |      0.000 |              0 |  59,721,406 |   0.000000 | LOX.INTERPRETER::CHECK-NUMBER-OPERANDS
     2.395 |      0.000 |              0 |  29,860,703 |   0.000000 | LOX.INTERPRETER::EVAL-TRUTHY-P
     0.062 |      0.000 |              0 |  29,860,703 |   0.000000 | LOX.INTERPRETER::TRUTHY-P
     0.001 |      0.000 |         65,520 |          35 |   0.000019 | LOX.RESOLVER:RESOLVE

下面是一些罪魁祸首:

;;; Related to lox-callable-arity:

;; defclass++ is a macro on top of defclass to add accessors and a default constructor
(defclass++ lox-native-function (lox-callable)
  ((name :type string)
   (arity :type integer)
   (fn :type function)
   (str-repr :type string)))

(defmethod lox-callable-arity ((callee lox-native-function))
  (slot-value callee 'arity))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; Related to type? and check-number-operands

(defun type? (type-specifier &rest vars)
  "Ensure all vars are of type type-specifier."
  (loop for var in vars always (typep var type-specifier)))

(defun* check-number-operands ((operator token:token) left right)
  (when (not (type? 'number left right))
    (error 'lox.error:lox-runtime-error
           :token operator
           :message (format nil "Operands of '~A' must be numbers." @operator.lexeme))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

问题:

谢谢:)

根据您的分析器输出,我假设您正在使用 SBCL。

type?引起的coning几乎肯定是&rest参数的结果。如果有的话,循环应该不足以解释您的分析结果。我 运行 进行了一些测试,发现包含两个参数的 &rest 参数的内存使用情况相似。您可以按照我在评论中提到的那样堆栈分配 vars 参数,或者重写函数以恰好采用您提到的三个参数。

lox-callable-arity 中的 consing 可能是由函数在返回对象时复制对象的 arity 槽引起的(我的测试似乎再次支持这个理论)。我认为当您手动定义 reader 函数时,它不会获得 SBCL 应用于 readers 和使用 defclass 插槽选项定义的访问器的一些优化,例如内联。您可能应该删除该定义并将 arity 插槽定义更改为 (arity :type integer :reader lox-callable-arity) 以更干净地为插槽获得优化的 reader 函数,或者将 (declaim (inline lox-callable-arity)) 放在方法定义之上。我的猜测是您的 defclass++ 宏使用 defmethod 来定义 class 的默认访问器。也应该改为使用插槽选项访问器或内联函数,以避免将来出现类似问题。