Liên kết từ điển so với ràng buộc động nói chung
Hãy xem xét ví dụ sau:
(let ((lexical-binding nil))
(disassemble
(byte-compile (lambda ()
(let ((foo 10))
(message foo))))))
Nó biên dịch và ngay lập tức phân tách một đơn giản lambda
với một biến cục bộ. Với lexical-binding
vô hiệu hóa, như trên, mã byte trông như sau:
0 constant 10
1 varbind foo
2 constant message
3 varref foo
4 call 1
5 unbind 1
6 return
Lưu ý varbind
và varref
hướng dẫn. Các hướng dẫn này liên kết và tra cứu các biến tương ứng theo tên của chúng trong môi trường liên kết toàn cầu trên bộ nhớ heap . Tất cả điều này có ảnh hưởng xấu đến hiệu suất: Nó bao gồm băm và so sánh chuỗi , đồng bộ hóa để truy cập dữ liệu toàn cầu và truy cập bộ nhớ heap lặp đi lặp lại , điều này rất tệ với bộ nhớ đệm CPU. Ngoài ra, các ràng buộc biến động cần phải được khôi phục về biến trước đó của chúng ở cuối let
, điều này bổ n
sung thêm tra cứu cho mỗi let
khối với n
các ràng buộc.
Nếu bạn ràng buộc lexical-binding
để t
trong ví dụ trên, các mã byte trông hơi khác:
0 constant 10
1 constant message
2 stack-ref 1
3 call 1
4 return
Lưu ý rằng varbind
và varref
hoàn toàn biến mất. Biến cục bộ chỉ đơn giản được đẩy lên ngăn xếp và được gọi bằng một giá trị bù không đổi thông qua stack-ref
hướng dẫn. Về cơ bản, biến bị ràng buộc và đọc với thời gian không đổi , bộ nhớ trong ngăn xếp đọc và ghi, hoàn toàn cục bộ và do đó chơi tốt với bộ đệm đồng thời và bộ đệm CPU , và không liên quan đến bất kỳ chuỗi nào.
Nói chung, với tra cứu từ vựng ràng buộc của các biến địa phương (ví dụ let
, setq
vv) có ít thời gian chạy và bộ nhớ phức tạp .
Ví dụ cụ thể này
Với ràng buộc động, mỗi người sẽ phải chịu một hình phạt hiệu suất, vì những lý do trên. Càng cho phép, các ràng buộc biến động càng nhiều.
Đáng chú ý, với một bổ sung let
trong loop
cơ thể, biến bị ràng buộc sẽ cần phải được khôi phục ở mỗi lần lặp của vòng lặp , thêm một tra cứu biến bổ sung cho mỗi lần lặp . Do đó, nhanh hơn để giữ cho phép thoát khỏi thân vòng lặp, do đó biến lặp chỉ được đặt lại một lần , sau khi toàn bộ vòng lặp kết thúc. Tuy nhiên, điều này không đặc biệt tao nhã, vì biến lặp được ràng buộc theo cách trước khi nó thực sự được yêu cầu.
Với ràng buộc từ vựng, let
s là giá rẻ. Đáng chú ý, một thân let
trong vòng lặp không tệ hơn (hiệu năng thông minh) so với let
bên ngoài thân vòng lặp. Do đó, việc liên kết các biến cục bộ càng tốt càng tốt và giữ biến lặp được giới hạn trong thân vòng lặp.
Nó cũng nhanh hơn một chút, vì nó biên dịch theo hướng dẫn ít hơn nhiều. Xem xét việc tháo gỡ bên cạnh theo sau (cục bộ ở bên phải):
0 varref list 0 varref list
1 constant nil 1:1 dup
2 varbind it 2 goto-if-nil-else-pop 2
3 dup 5 dup
4 varbind temp 6 car
5 goto-if-nil-else-pop 2 7 stack-ref 1
8:1 varref temp 8 cdr
9 car 9 discardN-preserve-tos 2
10 varset it 11 goto 1
11 varref temp 14:2 return
12 cdr
13 dup
14 varset temp
15 goto-if-not-nil 1
18 constant nil
19:2 unbind 2
20 return
Tôi không có manh mối, mặc dù, những gì gây ra sự khác biệt.
varbind
mã được biên dịch theo ràng buộc từ vựng. Đó là toàn bộ quan điểm và mục đích.