例如,如果循环正在运行,每次迭代都调用 '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: (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 object for FOO>
(与 #<FUNCTION FOO>
返回的对象 (fdefinition 'foo)
相反),所以,显然, fdefinition
在每次迭代中调用。
这可以通过比较这两个反汇编来确认:
* (disassemble (lambda () (fdefinition 'foo)))
; disassembly for (LAMBDA ())
; Size: 31 bytes. Origin: #x1002FF99F4
; 9F4: 488B15A5FFFFFF MOV RDX, [RIP-91] ; 'FOO
; no-arg-parsing entry point
; 9FB: 488B05A6FFFFFF MOV RAX, [RIP-90] ; #<FDEFINITION object for FDEFINITION>
; A02: B902000000 MOV ECX, 2
; A07: FF7508 PUSH QWORD PTR [RBP+8]
; A0A: FF6009 JMP QWORD PTR [RAX+9]
; A0D: 0F0B0A BREAK 10 ; error trap
; A10: 02 BYTE #X02
; A11: 19 BYTE #X19 ; INVALID-ARG-COUNT-ERROR
; A12: 9A BYTE #X9A ; RCX
NIL
* (disassemble (lambda () #.(fdefinition 'foo)))
; disassembly for (LAMBDA ())
; Size: 19 bytes. Origin: #x1003020214
; 14: 488B15A5FFFFFF MOV RDX, [RIP-91] ; #<FUNCTION FOO>
; no-arg-parsing entry point
; 1B: 488BE5 MOV RSP, RBP
; 1E: F8 CLC
; 1F: 5D POP RBP
; 20: C3 RET
; 21: 0F0B0A BREAK 10 ; error trap
; 24: 02 BYTE #X02
; 25: 19 BYTE #X19 ; INVALID-ARG-COUNT-ERROR
; 26: 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。
关于common-lisp - 如果正在使用该函数,SBCL 何时会用更新的版本替换已编译的函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25037033/