Common lisp 如果编译后的函数正在使用,SBCL什么时候用更新的版本替换该函数?
例如,如果一个循环在每次迭代时调用'FOO',而我在循环退出之前重新编译'FOO',会发生什么Common lisp 如果编译后的函数正在使用,SBCL什么时候用更新的版本替换该函数?,common-lisp,sbcl,Common Lisp,Sbcl,例如,如果一个循环在每次迭代时调用'FOO',而我在循环退出之前重新编译'FOO',会发生什么 SBCL用于处理此类情况的具体机制是什么?SBCL是一个仅编译的实现,因此您的问题的答案很容易找到: * (defun foo (x) (print x)) FOO * (describe 'foo) COMMON-LISP-USER::FOO [symbol] FOO names a compiled function: Lambda-list: (X) Derived type:
SBCL用于处理此类情况的具体机制是什么?SBCL是一个仅编译的实现,因此您的问题的答案很容易找到:
* (defun foo (x) (print x))
FOO
* (describe 'foo)
COMMON-LISP-USER::FOO
[symbol]
FOO names a compiled function:
Lambda-list: (X)
Derived type: (FUNCTION (T) (VALUES T &OPTIONAL))
Source form:
(SB-INT:NAMED-LAMBDA FOO
(X)
(BLOCK FOO (PRINT X)))
* (disassemble (lambda ()(loop repeat 10 do (foo 1))))
; disassembly for (LAMBDA ())
; Size: 91 bytes. Origin: #x1002F7F564
; 64: BE14000000 MOV ESI, 20 ; no-arg-parsing entry point
; 69: EB3E JMP L1
; 6B: 0F1F440000 NOP
; 70: L0: 488BCE MOV RCX, RSI
; 73: 4883E902 SUB RCX, 2
; 77: 488BF1 MOV RSI, RCX
; 7A: 488D5C24F0 LEA RBX, [RSP-16]
; 7F: 4883EC18 SUB RSP, 24
; 83: BA02000000 MOV EDX, 2
; 88: 488975F8 MOV [RBP-8], RSI
; 8C: 488B057DFFFFFF MOV RAX, [RIP-131] ; #<FDEFINITION object for FOO>
; 93: B902000000 MOV ECX, 2
; 98: 48892B MOV [RBX], RBP
; 9B: 488BEB MOV RBP, RBX
; 9E: FF5009 CALL QWORD PTR [RAX+9]
; A1: 480F42E3 CMOVB RSP, RBX
; A5: 488B75F8 MOV RSI, [RBP-8]
; A9: L1: 4885F6 TEST RSI, RSI
; AC: 7FC2 JNLE L0
; AE: BA17001020 MOV EDX, 537919511
; B3: 488BE5 MOV RSP, RBP
; B6: F8 CLC
; B7: 5D POP RBP
; B8: C3 RET
; B9: 0F0B0A BREAK 10 ; error trap
; BC: 02 BYTE #X02
; BD: 19 BYTE #X19 ; INVALID-ARG-COUNT-ERROR
; BE: 9A BYTE #X9A ; RCX
NIL
第一个肯定调用fdefinition
,第二个肯定不调用,第一个更接近循环的反汇编
最后,可以使用Paulo Madeira的显式测试:
(progn (sb-thread:make-thread (lambda () (sleep 5.1) (defun foo (x) (print (1+ x)))))
(dotimes (i 10) (sleep 1) (foo 1)))
开始显示2。这一定是某种优化的
fdefinition
访问器,因为(progn(sb线程:make线程(lambda()(sleep 5.1)(defun foo(x)(print(1+x‘))))(dotimes(i10)(sleep 1)(foo 1))
开始显示2
。哎呀,我猜我误解了反汇编。对不起,你问题的答案很容易找到,但这与SBCL的汇编无关;正如我们所看到的,仅仅基于拆卸并不是那么容易。一个更方便、更简单的测试就足够了,例如,(dotimes(i10)(foo1)(when(=i4)(defun foo(x)(print(1+x‘)))
。尽管如此,编译器还是可以推断,既然循环中有对foo
的重新定义,它将使用按名称查找来编译对foo
的调用,而不是在循环之前获取函数对象。即使使用线程,如果没有适当的内存屏障(取决于体系结构),循环线程也可能看不到新定义,但这是另一个主题。
(progn (sb-thread:make-thread (lambda () (sleep 5.1) (defun foo (x) (print (1+ x)))))
(dotimes (i 10) (sleep 1) (foo 1)))