【发布时间】:2020-06-14 23:34:54
【问题描述】:
我已经使用 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))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
问题:
- 是什么原因导致 lox-callable-arity 出现如此多的问题?
- 是什么导致了这么多类型的consing?
- 是循环吗?
- 参数
&rest args是否导致consing - 从left right构建列表args?- 我总是只传递类型旁边的 2 个参数,例如 check-number-operands 中的类型
- 定义一个只以
left right而不是&rest args作为参数的type?函数是否有性能优势?
- 我知道我可以用宏替换
type?,但我很困惑为什么程序中最不复杂的操作之一有这么大的权重。
谢谢你:)
【问题讨论】:
-
在
type?中,将vars声明为dynamic-extent可能会解决其内存问题。只需将(declare (dynamic-extent vars))放在文档字符串下方即可。这允许编译器堆栈分配变量并在函数返回时自动释放它,而无需涉及垃圾收集器。