Saya ingin berkongsi beberapa perkara yang cukup menarik Saya telah belajar tentang Kod bait Python dengan anda, termasuk cara saya menambah sokongan untuk nested berfungsi, tetapi lelaki saya di mesin cetak berkata saya perlu menyimpannya di bawah 500 patah perkataan.
Hari ini minggu cuti, dia mengangkat bahu. Apa yang anda harapkan saya lakukan?
Tidak termasuk coretan kod, saya tawar-menawar.
Baik, dia menyerah kalah.
Adakah anda tahu mengapa kami menggunakan bytecode sejak awal?
Saya baru sahaja mengendalikan mesin cetak, saya percayakan anda.
Cukup adil. Mari mulakan.
Memphis, jurubahasa Python saya yang ditulis dalam Rust, mempunyai dua enjin pelaksanaan. Kedua-duanya tidak boleh menjalankan semua kod tetapi kedua-duanya boleh menjalankan beberapa kod.
jurubahasa treewalk saya ialah apa yang anda akan bina jika anda tidak tahu apa yang anda lakukan. ?♂️ Anda menandakan kod Python input, menjana pepohon sintaks abstrak (AST), dan kemudian menjalani pepohon dan menilai setiap nod. Ungkapan mengembalikan nilai dan pernyataan mengubah suai jadual simbol, yang dilaksanakan sebagai satu siri skop yang menghormati peraturan skop Python. Ingat sahaja LEGB pneumonik mudah: tempatan, tertutup, global, terbina.
bytecode VM saya ialah perkara yang anda akan bina jika anda tidak tahu apa yang anda lakukan tetapi mahu bertindak seperti yang anda lakukan. Juga ?♂️. Untuk enjin ini, token dan AST berfungsi sama, tetapi daripada berjalan, kami mula berlari pecut. Kami menghimpun AST menjadi perwakilan perantaraan (IR) selepas ini dikenali sebagai bytecode. Kami kemudiannya mencipta mesin maya (VM) berasaskan tindanan, yang secara konsepnya bertindak seperti CPU, melaksanakan arahan kod bait mengikut turutan, tetapi ia dilaksanakan sepenuhnya dalam perisian.
(Untuk panduan lengkap kedua-dua pendekatan tanpa bertele-tele, Jurubahasa Kerajinan adalah cemerlang.)
Mengapa kita melakukan ini pada mulanya? Hanya ingat dua P: mudah alih dan prestasi. Ingat bagaimana pada awal 2000-an tiada siapa yang akan menutup mulut tentang bagaimana kod bait Java mudah alih? Apa yang anda perlukan hanyalah JVM dan anda boleh menjalankan program Java yang disusun pada mana-mana mesin! Python memilih untuk tidak menggunakan pendekatan ini atas sebab teknikal dan pemasaran, tetapi secara teori prinsip yang sama digunakan. (Secara praktikal, langkah penyusunan adalah berbeza dan saya menyesal membuka tin cacing ini.)
Prestasi adalah masalah besar. Daripada merentasi AST berbilang kali sepanjang hayat program, IR yang disusun adalah perwakilan yang lebih cekap. Kami melihat prestasi yang lebih baik daripada mengelakkan overhed berulang kali melintasi AST, dan struktur ratanya sering menghasilkan ramalan cawangan dan lokasi cache yang lebih baik pada masa jalan.
(Saya tidak menyalahkan anda kerana tidak memikirkan tentang caching jika anda tidak mempunyai latar belakang dalam seni bina komputer—apakah, saya memulakan kerjaya saya dalam industri itu dan saya berfikir tentang caching jauh lebih sedikit daripada yang saya fikirkan tentang cara untuk mengelakkan menulis baris kod yang sama dua kali. Jadi percayakan saya pada bahagian prestasi itu: kepercayaan buta.)
Hai kawan, itu 500 patah perkataan. Kita perlu memuatkan bingkai dan biarkan koyak.
Sudah?! Anda mengecualikan coretan kod?
Tiada coretan kod, kawanku.
Okay okay. Hanya 500 lagi. Saya berjanji.
Saya mendapat agak jauh sebelum membentangkan pelaksanaan VM bytecode saya kira-kira setahun yang lalu: Saya boleh mentakrifkan fungsi dan kelas Python serta memanggil fungsi tersebut dan membuat contoh kelas tersebut. Saya mengekang tingkah laku ini dengan beberapa ujian. Tetapi saya tahu pelaksanaan saya tidak kemas dan saya perlu menyemak semula asas sebelum menambah lebih banyak perkara yang menyeronokkan. Sekarang minggu Krismas dan saya ingin menambah bahan yang menyeronokkan.
Pertimbangkan coretan ini untuk memanggil fungsi, memerhatikan TODO.
fn compile_function_call( &mut self, name: &str, args: &ParsedArguments) ) -> Result<Bytecode, CompileError> { let mut opcodes = vec![]; // We push the args onto the stack in reverse call order so that we will pop // them off in call order. for arg in args.args.iter().rev() { opcodes.extend(self.compile_expr(arg)?); } let (_, index) = self.get_local_index(name); // TODO how does this know if it is a global or local index? this may not be the right // approach for calling a function opcodes.push(Opcode::Call(index)); Ok(opcodes) }
Adakah anda selesai mempertimbangkan? Kami memuatkan hujah fungsi ke dalam timbunan dan "panggil fungsi". Dalam bytecode, semua nama ditukar kepada indeks (kerana akses indeks lebih pantas semasa masa jalan VM), tetapi kami tidak benar-benar mempunyai cara untuk mengetahui sama ada kami berurusan dengan indeks tempatan atau indeks global di sini.
Sekarang pertimbangkan versi yang dipertingkatkan.
fn compile_function_call( &mut self, name: &str, args: &ParsedArguments) ) -> Result<Bytecode, CompileError> { let mut opcodes = vec![self.compile_load(name)]; // We push the args onto the stack in reverse call order so that we will pop // them off in call order. for arg in args.args.iter().rev() { opcodes.extend(self.compile_expr(arg)?); } let argc = opcodes.len() - 1; opcodes.push(Opcode::Call(argc)); Ok(opcodes) }
Terima kasih kerana mempertimbangkan kod itu.
Kami kini menyokong panggilan fungsi bersarang! Apa yang berubah?
Mari kita lihat apa yang dilakukan oleh compile_load.
fn compile_load(&mut self, name: &str) -> Opcode { match self.ensure_context() { Context::Global => Opcode::LoadGlobal(self.get_or_set_nonlocal_index(name)), Context::Local => { // Check locals first if let Some(index) = self.get_local_index(name) { return Opcode::LoadFast(index); } // If not found locally, fall back to globals Opcode::LoadGlobal(self.get_or_set_nonlocal_index(name)) } } }
Terdapat beberapa prinsip utama dalam tindakan di sini:
Perkara terakhir yang saya akan tinggalkan kepada anda hari ini ialah melihat bagaimana nama pembolehubah ini dipetakan. Dalam coretan kod di bawah, anda akan melihat bahawa indeks tempatan ditemui dalam code.varnames dan indeks bukan tempatan ditemui dalam code.names. Kedua-duanya hidup pada CodeObject, yang mengandungi metadata untuk blok kod bait Python, termasuk pembolehubah dan pemetaan namanya.
fn compile_function_call( &mut self, name: &str, args: &ParsedArguments) ) -> Result<Bytecode, CompileError> { let mut opcodes = vec![]; // We push the args onto the stack in reverse call order so that we will pop // them off in call order. for arg in args.args.iter().rev() { opcodes.extend(self.compile_expr(arg)?); } let (_, index) = self.get_local_index(name); // TODO how does this know if it is a global or local index? this may not be the right // approach for calling a function opcodes.push(Opcode::Call(index)); Ok(opcodes) }
Perbezaan antara nama vakar dan nama menyeksa saya selama berminggu-minggu (CPython memanggil nama_varnamen dan nama_bersama ini), tetapi ia sebenarnya agak mudah. vaname menyimpan nama pembolehubah untuk semua pembolehubah tempatan dalam skop tertentu dan nama melakukan perkara yang sama untuk semua bukan tempatan.
Setelah kami menjejaki ini dengan betul, semua yang lain hanya berfungsi. Pada masa jalanan, VM melihat LOAD_GLOBAL atau LOAD_FAST dan tahu untuk melihat dalam kamus ruang nama global atau tindanan setempat, masing-masing.
Kawan! Encik Gutenberg sedang menelefon dan berkata kami tidak boleh menekan menekan lagi.
Baiklah! baiklah! Saya faham! Mari hantar. ?
Shh! Lelaki mesin cetak tidak tahu saya sedang menulis kesimpulan, jadi saya akan ringkas.
Dengan skop pembolehubah dan panggilan fungsi di tempat yang kukuh, saya secara beransur-ansur mengalihkan perhatian saya kepada ciri seperti surih tindanan dan sokongan tak segerak. Jika anda suka menyelami kod bait ini atau mempunyai soalan tentang membina penterjemah anda sendiri, saya ingin mendengar daripada anda—berikan ulasan!
Langgan & Simpan [pada apa-apa]
Jika anda ingin mendapatkan lebih banyak siaran seperti ini terus ke peti masuk anda, anda boleh melanggan di sini!
Bekerja Dengan Saya
Saya mentor jurutera perisian untuk menavigasi cabaran teknikal dan pertumbuhan kerjaya dalam persekitaran yang kadangkala bodoh yang menyokong. Jika anda berminat, anda boleh menempah sesi di sini.
Di tempat lain
Selain bimbingan, saya juga menulis tentang pengalaman saya mengemudi bekerja sendiri dan autisme yang didiagnosis lewat. Kurang kod dan bilangan jenaka yang sama.
Atas ialah kandungan terperinci Bagaimana saya menambah sokongan untuk fungsi bersarang dalam Python bytecode. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!