Mengikat leksikal versus pengikatan dinamis secara umum
Perhatikan contoh berikut:
(let ((lexical-binding nil))
(disassemble
(byte-compile (lambda ()
(let ((foo 10))
(message foo))))))
Ini mengkompilasi dan segera membongkar sederhana lambdadengan variabel lokal. Dengan lexical-bindingdinonaktifkan, seperti di atas, kode byte terlihat sebagai berikut:
0 constant 10
1 varbind foo
2 constant message
3 varref foo
4 call 1
5 unbind 1
6 return
Perhatikan varbinddan varrefinstruksinya. Instruksi ini mengikat dan mencari masing-masing variabel dengan namanya di lingkungan pengikatan global pada memori tumpukan . Semua ini memiliki efek buruk pada kinerja: Ini melibatkan string hashing dan perbandingan , sinkronisasi untuk akses data global, dan akses memori tumpukan berulang yang bermain buruk dengan caching CPU. Juga, binding variabel dinamis perlu dikembalikan ke variabel sebelumnya di akhir let, yang menambahkan npencarian tambahan untuk setiap letblok dengan nbinding.
Jika Anda mengikat lexical-bindinguntuk tdi contoh di atas, kode byte terlihat agak berbeda:
0 constant 10
1 constant message
2 stack-ref 1
3 call 1
4 return
Catat itu varbinddan varrefsepenuhnya hilang. Variabel lokal hanya didorong ke stack, dan disebut dengan offset konstan melalui stack-refinstruksi. Pada dasarnya, variabel terikat dan dibaca dengan waktu konstan , memori in-stack membaca dan menulis, yang sepenuhnya lokal dan dengan demikian bermain baik dengan konkurensi dan caching CPU , dan tidak melibatkan string sama sekali.
Umumnya, dengan pencarian mengikat leksikal variabel lokal (misalnya let, setq, dll) memiliki lebih sedikit runtime dan memori kompleksitas .
Contoh spesifik ini
Dengan pengikatan dinamis, masing-masing membiarkan dikenakan penalti kinerja, untuk alasan di atas. Semakin banyak memungkinkan, binding variabel lebih dinamis.
Khususnya, dengan tambahan letdi dalam looptubuh, variabel terikat perlu dikembalikan pada setiap iterasi dari loop , menambahkan pencarian variabel tambahan untuk setiap iterasi . Oleh karena itu, lebih cepat untuk menjaga keluar dari tubuh loop, sehingga variabel iterasi hanya direset sekali , setelah seluruh loop selesai. Namun, ini tidak terlalu elegan, karena variabel iterasi terikat sebelum benar-benar diperlukan.
Dengan pengikatan leksikal, lets itu murah. Khususnya, di letdalam tubuh loop tidak lebih buruk (kinerja-bijaksana) daripada di letluar tubuh loop. Oleh karena itu, sangat baik untuk mengikat variabel secara lokal mungkin, dan menjaga variabel iterasi terbatas pada badan loop.
Ini juga sedikit lebih cepat, karena mengkompilasi instruksi jauh lebih sedikit. Pertimbangkan pembongkaran berdampingan yang diikuti (biarkan lokal di sisi kanan):
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
Saya tidak tahu, apa yang menyebabkan perbedaan.
varbindkode yang dikompilasi di bawah pengikatan leksikal. Itulah inti dan tujuannya.