cari
Rumahpembangunan bahagian belakangTutorial PythonMeningkatkan kecekapan ingatan dalam penterjemah yang berfungsi

Improving memory efficiency in a working interpreter

Hayat hidup ialah ciri yang menarik bagi Rust dan pengalaman manusia. Ini adalah blog teknikal, jadi mari kita fokus pada yang pertama. Saya diakui adalah pengguna yang lambat untuk memanfaatkan jangka hayat untuk meminjam data dengan selamat dalam Rust. Dalam pelaksanaan treewalk Memphis, penterjemah Python saya yang ditulis dalam Rust, saya hampir tidak memanfaatkan jangka hayat (dengan mengklon tanpa henti) dan saya berulang kali mengelak pemeriksa pinjaman (dengan menggunakan kebolehubah dalaman, juga tanpa henti) apabila boleh.

Rakyat Rustacean saya, saya di sini hari ini untuk memberitahu anda bahawa ini berakhir sekarang. Baca bibir saya…tiada jalan pintas lagi.

Okey okey, biar betul. Apa itu jalan pintas berbanding cara yang betul adalah soal keutamaan dan perspektif. Kita semua telah melakukan kesilapan, dan saya di sini untuk bertanggungjawab ke atas diri saya.

Saya mula menulis jurubahasa enam minggu selepas saya mula-mula memasang rustc kerana saya tidak mempunyai kesejukan. Dengan sikap yang meleret-leret dan postur itu, mari kita mulakan kuliah hari ini tentang cara kita boleh menggunakan jangka hayat sebagai talian hayat kita untuk menambah baik pangkalan kod jurubahasa saya yang kembung.

Mengenal pasti dan mengelakkan data klon

Satu Hayat karat ialah mekanisme yang menyediakan jaminan masa kompilasi bahawa sebarang rujukan tidak melebihi umur objek yang dirujuknya. Ia membolehkan kita mengelakkan masalah "penunjuk berjuntai" daripada C dan C .

Ini mengandaikan anda memanfaatkannya sama sekali! Pengklonan ialah penyelesaian yang mudah apabila anda ingin mengelakkan kerumitan yang berkaitan dengan pengurusan jangka hayat, walaupun kelemahannya ialah peningkatan penggunaan memori dan sedikit kelewatan yang berkaitan dengan setiap kali data disalin.

Menggunakan seumur hidup juga memaksa anda untuk berfikir lebih idiomatik tentang pemilik dan meminjam dalam Rust, yang saya ingin lakukan.

Saya memilih calon pertama saya sebagai token daripada fail input Python. Pelaksanaan asal saya, yang sangat bergantung pada panduan ChatGPT semasa saya duduk di Amtrak, menggunakan aliran ini:

  1. kami menghantar teks Python kami kepada Builder
  2. Pembina mencipta Lexer, yang menandakan aliran input
  3. Pembina kemudian mencipta Parser, yang mengklonkan aliran token untuk menyimpan salinannya sendiri
  4. Pembina digunakan untuk mencipta Jurubahasa, yang berulang kali meminta Parser untuk pernyataan yang dihuraikan seterusnya dan menilainya sehingga kami sampai ke penghujung aliran token

Aspek yang mudah untuk mengklon aliran token ialah Lexer bebas untuk digugurkan selepas langkah 3. Dengan mengemas kini seni bina saya supaya Lexer memiliki token dan Parser hanya meminjamnya, Lexer kini perlu kekal hidup lebih lama lagi. Jangka hayat karat akan menjamin ini untuk kami: selagi Parser wujud memegang rujukan kepada token yang dipinjam, pengkompil akan menjamin bahawa Lexer yang memiliki token tersebut masih wujud, memastikan rujukan yang sah.

Seperti semua kod selalu, ini akhirnya menjadi perubahan yang lebih besar daripada yang saya jangkakan. Mari lihat sebabnya!

Penghurai baharu

Sebelum mengemas kini Parser untuk meminjam token daripada Lexer, ia kelihatan seperti ini. Dua bidang yang diminati untuk perbincangan hari ini ialah token dan current_token. Kami tidak tahu berapa besar Vec adalah, tetapi ia jelas milik kami (iaitu kami tidak meminjamnya).

pub struct Parser {
    state: Container<state>,
    tokens: Vec<token>,
    current_token: Token,
    position: usize,
    line_number: usize,
    delimiter_depth: usize,
}

impl Parser {
    pub fn new(tokens: Vec<token>, state: Container<state>) -> Self {
        let current_token = tokens.first().cloned().unwrap_or(Token::Eof);
        Parser {
            state,
            tokens,
            current_token,
            position: 0,
            line_number: 1,
            delimiter_depth: 0,
        }
    }
}
</state></token></token></state>

Selepas meminjam token daripada Lexer, ia kelihatan agak serupa, tetapi kini kita melihat SEUMUR HIDUP! Dengan menyambungkan token kepada 'a seumur hidup, pengkompil Rust tidak akan membenarkan pemilik token (iaitu Lexer kami) dan token itu sendiri digugurkan semasa Parser kami masih merujuknya. Ini rasa selamat dan mewah!

static EOF: Token = Token::Eof;

/// A recursive-descent parser which attempts to encode the full Python grammar.
pub struct Parser {
    state: Container<state>,
    tokens: &'a [Token],
    current_token: &'a Token,
    position: usize,
    line_number: usize,
    delimiter_depth: usize,
}

impl Parser {
    pub fn new(tokens: &'a [Token], state: Container<state>) -> Self {
        let current_token = tokens.first().unwrap_or(&EOF);
        Parser {
            state,
            tokens,
            current_token,
            position: 0,
            line_number: 1,
            delimiter_depth: 0,
        }
    }
}
</state></state>

Satu lagi perbezaan kecil yang anda mungkin perasan ialah baris ini:

static EOF: Token = Token::Eof;

Ini adalah pengoptimuman kecil yang saya mula pertimbangkan sebaik sahaja Penghurai saya bergerak ke arah "cekap ingatan". Daripada membuat instantiat Token baharu::Eof setiap kali Parser perlu menyemak sama ada ia berada di penghujung strim teks, model baharu membenarkan saya membuat instantiat hanya satu token dan rujukan &EOF berulang kali.

Sekali lagi, ini adalah pengoptimuman kecil, tetapi ia merujuk kepada pemikiran yang lebih besar bagi setiap data yang wujud hanya sekali dalam ingatan dan setiap pengguna hanya merujuknya apabila diperlukan, yang mana Rust menggalakkan anda untuk melakukannya dan memegang tangan anda dengan kemas caranya.

Bercakap tentang pengoptimuman, saya sepatutnya menanda aras penggunaan memori sebelum dan selepas. Memandangkan saya tidak melakukannya, saya tidak mempunyai apa-apa lagi untuk diperkatakan mengenai perkara itu.

Seperti yang saya nyatakan sebelum ini, mengikat seumur hidup Lexer dan Parser saya bersama-sama memberi impak yang besar pada corak Builder saya. Mari lihat rupanya!

Pembina baharu: MemphisContext

Dalam aliran yang saya terangkan di atas, ingat bagaimana saya menyebut bahawa Lexer boleh digugurkan sebaik sahaja Parser mencipta salinan tokennya sendiri? Ini secara tidak sengaja telah mempengaruhi reka bentuk Builder saya, yang bertujuan untuk menjadi komponen yang menyokong mendalangi interaksi Lexer, Parser dan Interpreter, sama ada anda bermula dengan strim teks Python atau laluan ke fail Python.

Seperti yang anda lihat di bawah, terdapat beberapa aspek lain yang tidak sesuai untuk reka bentuk ini:

  1. perlu memanggil kaedah downcast berbahaya untuk mendapatkan Jurubahasa.
  2. kenapa saya fikir tidak mengapa untuk memulangkan Parser kepada setiap ujian unit hanya untuk meneruskannya terus ke interpreter.run(&mut parser)?!
fn downcast<t: interpreterentrypoint>(input: T) -> Interpreter {
    let any_ref: &dyn Any = &input as &dyn Any;
    any_ref.downcast_ref::<interpreter>().unwrap().clone()
}

fn init(text: &str) -> (Parser, Interpreter) {
    let (parser, interpreter) = Builder::new().text(text).build();

    (parser, downcast(interpreter))
}


#[test]
fn function_definition() {
     let input = r#"
def add(x, y):
    return x + y

a = add(2, 3)
"#;
    let (mut parser, mut interpreter) = init(input);

    match interpreter.run(&mut parser) {
        Err(e) => panic!("Interpreter error: {:?}", e),
        Ok(_) => {
            assert_eq!(
                interpreter.state.read("a"),
                Some(ExprResult::Integer(5.store()))
            );
        }
    }
}
</interpreter></t:>

Di bawah ialah antara muka MemphisContext baharu. Mekanisme ini menguruskan seumur hidup Lexer secara dalaman (untuk memastikan rujukan kami kekal cukup lama untuk memastikan Parser kami gembira!) dan hanya mendedahkan perkara yang diperlukan untuk menjalankan ujian ini.

pub struct Parser {
    state: Container<state>,
    tokens: Vec<token>,
    current_token: Token,
    position: usize,
    line_number: usize,
    delimiter_depth: usize,
}

impl Parser {
    pub fn new(tokens: Vec<token>, state: Container<state>) -> Self {
        let current_token = tokens.first().cloned().unwrap_or(Token::Eof);
        Parser {
            state,
            tokens,
            current_token,
            position: 0,
            line_number: 1,
            delimiter_depth: 0,
        }
    }
}
</state></token></token></state>

context.run_and_return_interpreter() masih agak kikuk dan bercakap kepada masalah reka bentuk lain yang mungkin saya atasi: apabila anda menjalankan penterjemah, adakah anda mahu memulangkan hanya nilai pulangan akhir atau sesuatu yang membolehkan anda mengakses nilai sewenang-wenangnya daripada jadual simbol? Kaedah ini memilih pendekatan yang terakhir. Saya sebenarnya berpendapat ada kes untuk melakukan kedua-duanya, dan akan terus mengubahsuai API saya untuk membenarkan perkara ini semasa kita pergi.

Secara kebetulan, perubahan ini meningkatkan keupayaan saya untuk menilai sekeping kod Python yang sewenang-wenangnya. Jika anda ingat dari saga WebAssembly saya, saya terpaksa bergantung pada semak silang TreewalkAdapter saya untuk melakukannya pada masa itu. Kini, antara muka Wasm kami jauh lebih bersih.

static EOF: Token = Token::Eof;

/// A recursive-descent parser which attempts to encode the full Python grammar.
pub struct Parser {
    state: Container<state>,
    tokens: &'a [Token],
    current_token: &'a Token,
    position: usize,
    line_number: usize,
    delimiter_depth: usize,
}

impl Parser {
    pub fn new(tokens: &'a [Token], state: Container<state>) -> Self {
        let current_token = tokens.first().unwrap_or(&EOF);
        Parser {
            state,
            tokens,
            current_token,
            position: 0,
            line_number: 1,
            delimiter_depth: 0,
        }
    }
}
</state></state>

Konteks antara muka.evaluate_oneshot() mengembalikan hasil ungkapan dan bukannya jadual simbol penuh. Saya tertanya-tanya sama ada terdapat cara yang lebih baik untuk memastikan mana-mana kaedah "oneshot" hanya boleh beroperasi pada konteks sekali, memastikan tiada pengguna menggunakannya dalam konteks stateful. Saya akan terus merenungnya!

Adakah ini berbaloi?

Memphis ialah latihan pembelajaran yang pertama sekali, jadi ini sangat berbaloi!

Selain berkongsi token antara Lexer dan Parser, saya mencipta antara muka untuk menilai kod Python dengan boilerplate yang kurang ketara. Walaupun perkongsian data memperkenalkan kerumitan tambahan, perubahan ini membawa faedah yang jelas: penggunaan memori yang dikurangkan, jaminan keselamatan yang dipertingkatkan melalui pengurusan seumur hidup yang lebih ketat dan API diperkemas yang lebih mudah untuk diselenggara dan dilanjutkan.

Saya memilih untuk mempercayai ini adalah pendekatan yang betul, kebanyakannya untuk mengekalkan harga diri saya. Akhirnya, saya berhasrat untuk menulis kod yang menggambarkan dengan jelas prinsip perisian dan kejuruteraan komputer. Kami kini boleh membuka sumber Memphis, menunjuk kepada pemilik tunggal token dan tidur dengan nyenyak pada waktu malam!

Langgan & Simpan [pada apa-apa]

Jika anda ingin mendapatkan lebih banyak siaran seperti ini terus ke peti masuk anda, anda boleh melanggan di sini!

Di tempat lain

Selain membimbing jurutera perisian, saya juga menulis tentang pengalaman saya mengemudi bekerja sendiri dan autisme yang didiagnosis lewat. Kurang kod dan bilangan jenaka yang sama.

  • Kopi Kesan Tasik, Bab 1 - Dari titik awal org

Atas ialah kandungan terperinci Meningkatkan kecekapan ingatan dalam penterjemah yang berfungsi. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Bagaimana anda menambah elemen ke senarai python?Bagaimana anda menambah elemen ke senarai python?May 04, 2025 am 12:17 AM

ToAppendElementStoapyThonList, useTheAppend () methodforsingleelements, extend () formultipleelements, andInsert () forspecificposition.1) useAppend () foraddingOneElementAttheend.2)

Bagaimana anda membuat senarai python? Beri contoh.Bagaimana anda membuat senarai python? Beri contoh.May 04, 2025 am 12:16 AM

TOCREATEAPYTHONLIST, USESQUAREBRACKETS [] danSeparatateItemSwithCommas.1) listsaredynamicandCanHoldMixedDatypes.2) UseAppend (), mengalih keluar (), danSlicingFormApulation.3)

Bincangkan kes penggunaan dunia sebenar di mana penyimpanan dan pemprosesan data berangka yang cekap adalah kritikal.Bincangkan kes penggunaan dunia sebenar di mana penyimpanan dan pemprosesan data berangka yang cekap adalah kritikal.May 04, 2025 am 12:11 AM

Dalam bidang kewangan, penyelidikan saintifik, penjagaan perubatan dan AI, adalah penting untuk menyimpan dan memproses data berangka dengan cekap. 1) Dalam Kewangan, menggunakan memori yang dipetakan fail dan perpustakaan Numpy dapat meningkatkan kelajuan pemprosesan data dengan ketara. 2) Dalam bidang penyelidikan saintifik, fail HDF5 dioptimumkan untuk penyimpanan data dan pengambilan semula. 3) Dalam penjagaan perubatan, teknologi pengoptimuman pangkalan data seperti pengindeksan dan pembahagian meningkatkan prestasi pertanyaan data. 4) Dalam AI, data sharding dan diedarkan latihan mempercepatkan latihan model. Prestasi dan skalabiliti sistem dapat ditingkatkan dengan ketara dengan memilih alat dan teknologi yang tepat dan menimbang perdagangan antara kelajuan penyimpanan dan pemprosesan.

Bagaimana anda membuat array python? Beri contoh.Bagaimana anda membuat array python? Beri contoh.May 04, 2025 am 12:10 AM

Pythonarraysarecreatedusingthearraymodule, notbuilt-inlikelists.1) importTheArrayModule.2) specifythetypecode, cth., 'I'forintegers.3) Initializewithvalues.arraysofferbettermemoryficiencyficorhomogeneousdatabutflex.

Apakah beberapa alternatif untuk menggunakan garis shebang untuk menentukan penterjemah python?Apakah beberapa alternatif untuk menggunakan garis shebang untuk menentukan penterjemah python?May 04, 2025 am 12:07 AM

Sebagai tambahan kepada garis shebang, terdapat banyak cara untuk menentukan penterjemah python: 1. Gunakan perintah python terus dari baris arahan; 2. Gunakan fail batch atau skrip shell; 3. Gunakan alat binaan seperti membuat atau cmake; 4. Gunakan pelari tugas seperti Invoke. Setiap kaedah mempunyai kelebihan dan kekurangannya, dan penting untuk memilih kaedah yang sesuai dengan keperluan projek.

Bagaimanakah pilihan antara senarai dan tatasusunan memberi kesan kepada prestasi keseluruhan aplikasi Python yang berurusan dengan dataset yang besar?Bagaimanakah pilihan antara senarai dan tatasusunan memberi kesan kepada prestasi keseluruhan aplikasi Python yang berurusan dengan dataset yang besar?May 03, 2025 am 12:11 AM

Forhandlinglargedatasetsinpython, usenumpyarraysforbetterperformance.1) numpyarraysarememory-efisien danfasterfornumumerical.2) mengelakkan yang tidak dapat dipertahankan.3)

Jelaskan bagaimana memori diperuntukkan untuk senarai berbanding tatasusunan dalam Python.Jelaskan bagaimana memori diperuntukkan untuk senarai berbanding tatasusunan dalam Python.May 03, 2025 am 12:10 AM

Inpython, listsusedynamicMemoryAllocationwithover-peruntukan, pemecahan yang tidak dapat dilaksanakan.1) listsallocatemoremoremorythanneedinitial, resizingwhennessary.2) numpyarraysallocateExactMemoreForelements, menawarkanpredictableSabeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeBeat.

Bagaimana anda menentukan jenis data elemen dalam array python?Bagaimana anda menentukan jenis data elemen dalam array python?May 03, 2025 am 12:06 AM

Inpython, YouCansspectHedatypeyFeleMeremodelerernspant.1) Usenpynernrump.1) usenpynerp.dloatp.ploatm64, formor preciscontrolatatypes.

See all articles

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

Video Face Swap

Video Face Swap

Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Alat panas

Versi Mac WebStorm

Versi Mac WebStorm

Alat pembangunan JavaScript yang berguna

MinGW - GNU Minimalis untuk Windows

MinGW - GNU Minimalis untuk Windows

Projek ini dalam proses untuk dipindahkan ke osdn.net/projects/mingw, anda boleh terus mengikuti kami di sana. MinGW: Port Windows asli bagi GNU Compiler Collection (GCC), perpustakaan import yang boleh diedarkan secara bebas dan fail pengepala untuk membina aplikasi Windows asli termasuk sambungan kepada masa jalan MSVC untuk menyokong fungsi C99. Semua perisian MinGW boleh dijalankan pada platform Windows 64-bit.

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

Persekitaran pembangunan bersepadu PHP yang berkuasa

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

EditPlus versi Cina retak

EditPlus versi Cina retak

Saiz kecil, penyerlahan sintaks, tidak menyokong fungsi gesaan kod