Genel olarak dinamik bağlamaya karşı sözcüksel bağlanma
Aşağıdaki örneği düşünün:
(let ((lexical-binding nil))
(disassemble
(byte-compile (lambda ()
(let ((foo 10))
(message foo))))))
lambda
Yerel bir değişkeni olan basit bir nesneyi derler ve hemen demonte eder . İle lexical-binding
engelli yukarıdaki gibi, bayt kod görünüyor şöyle:
0 constant 10
1 varbind foo
2 constant message
3 varref foo
4 call 1
5 unbind 1
6 return
Not varbind
ve varref
talimatları. Bu talimatlar bağlamak ve arama sırasıyla değişkenler kendi adıyla bir de küresel üzerindeki bağlanma çevre yığın belleğe . Bütün bunlar performans üzerinde olumsuz bir etkiye sahiptir: Dize hashini ve karşılaştırmasını , global veri erişimi için senkronizasyonu ve CPU önbelleklemesi ile kötü oynayan tekrarlanan yığın bellek erişimini içerir. Ayrıca, dinamik değişken bağlantılarının , sonunda bağları olan her blok için ek aramalar ekleyen, önceki değişkenlerine geri getirilmesi gerekir .let
n
let
n
Eğer bağlama lexical-binding
için t
yukarıdaki örnekte, bayt kod biraz farklı görünüyor:
0 constant 10
1 constant message
2 stack-ref 1
3 call 1
4 return
Bunu varbind
ve varref
tamamen gittiğini unutmayın. Yerel değişken basitçe istifin üzerine itilir ve stack-ref
komut aracılığıyla sabit bir ofset ile ifade edilir . Esas olarak, değişken bağlanmış ile okunur sabit zamanda , içinde yığın bellek okur ve böylece tamamen yerel ve yazma, eşzamanlılık ve CPU önbelleğe alma ile de oynar ve hiç bir dizeleri anlamına gelmez.
Genel olarak, yerel değişkenlerin sözcük bağlanma aramaları (örn ile let
, setq
vs.) sahip çok daha az çalışma zamanı ve bellek karmaşıklığı .
Bu özel örnek
Dinamik bağlamada, her biri yukarıdaki sebeplerden dolayı bir performans cezasına çarptırılır. Ne kadar çok olursa, değişken değişken bağlamalar o kadar fazla olur.
Özellikle, gövde let
içinde bir ek ile loop
, sınır değişkeninin, her bir yinelemeye ek bir değişken araması ekleyerek , döngünün her yinelemesinde geri yüklenmesi gerekir . Bu nedenle, döngü gövdesinden uzak tutulması daha hızlıdır, böylece yineleme değişkeni tüm döngü bittikten sonra yalnızca bir kez sıfırlanır . Bununla birlikte, bu özellikle zarif değildir, çünkü yineleme değişkeni gerçekte gerekmeden önce bağlanır.
Sözlüksel bağlamayla, let
s ucuzdur. Özellikle, bir let
ilmek gövdesi içindeki bir ilmek, ilmek gövdesinin let
dış yüzeyinden daha kötü değildir (performans açısından) . Bu nedenle, değişkenleri olabildiğince yerel olarak bağlamak ve yineleme değişkeninin döngü gövdesiyle sınırlı kalması tamamen iyidir.
Aynı zamanda biraz daha hızlı, çünkü daha az talimat alıyor. Takip eden yan yana sökmeyi göz önünde bulundurun (sağ taraftaki yerel izin):
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
Ne var ki, aradaki farka neden olan hiçbir fikrim yok.
varbind
Sözcüksel bağlanma altında derlenmiş kod yoktur . Bütün amaç ve amaç bu.